1 // Copyright (c) 2013-2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.xwalk.core.internal;
7 import android.app.Activity;
8 import android.app.AlertDialog;
9 import android.app.ApplicationErrorReport;
10 import android.content.Context;
11 import android.content.DialogInterface;
12 import android.content.Intent;
13 import android.graphics.Paint;
14 import android.graphics.Rect;
15 import android.net.Uri;
16 import android.os.Bundle;
17 import android.os.Environment;
18 import android.os.Looper;
19 import android.provider.MediaStore;
20 import android.util.AttributeSet;
21 import android.util.Log;
22 import android.view.KeyEvent;
23 import android.view.ViewGroup;
24 import android.webkit.ValueCallback;
25 import android.widget.FrameLayout;
28 import java.io.IOException;
29 import java.io.PrintWriter;
30 import java.io.StringWriter;
31 import java.lang.ref.WeakReference;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Date;
36 import org.chromium.base.ActivityState;
37 import org.chromium.base.ApplicationStatus;
38 import org.chromium.base.ApplicationStatus.ActivityStateListener;
39 import org.chromium.base.CommandLine;
40 import org.xwalk.core.internal.extension.BuiltinXWalkExtensions;
43 * <p>XWalkViewInternal represents an Android view for web apps/pages. Thus most of attributes
44 * for Android view are valid for this class. Since it internally uses
45 * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
46 * android.view.SurfaceView</a> for rendering web pages by default, it can't be resized,
47 * rotated, transformed and animated due to the limitations of SurfaceView.
48 * Alternatively, if the preference key {@link XWalkPreferencesInternal#ANIMATABLE_XWALK_VIEW}
49 * is set to True, XWalkViewInternal can be transformed and animated because
50 * <a href="http://developer.android.com/reference/android/view/TextureView.html">
51 * TextureView</a> is intentionally used to render web pages for animation support.
52 * Besides, XWalkViewInternal won't be rendered if it's invisible.</p>
54 * <p>XWalkViewInternal needs hardware acceleration to render web pages. As a result, the
55 * AndroidManifest.xml of the caller's app must be appended with the attribute
56 * "android:hardwareAccelerated" and its value must be set as "true".</p>
58 * <application android:name="android.app.Application" android:label="XWalkUsers"
59 * android:hardwareAccelerated="true">
62 * <p>Crosswalk provides 2 major callback classes, namely {@link XWalkResourceClientInternal} and
63 * {@link XWalkUIClientInternal} for listening to the events related to resource loading and UI.
64 * By default, Crosswalk has a default implementation. Callers can override them if needed.</p>
66 * <p>Unlike other Android views, this class has to listen to system events like intents and activity result.
67 * The web engine inside this view need to get and handle them.
68 * With contianer activity's lifecycle change, XWalkViewInternal will pause all timers and other
69 * components like videos when activity paused, resume back them when activity resumed.
70 * When activity is about to destroy, XWalkViewInternal will destroy itself as well.
71 * Embedders can also call onHide() and pauseTimers() to explicitly pause XWalkViewInternal.
72 * Similarily with onShow(), resumeTimers() and onDestroy().
77 * import android.app.Activity;
78 * import android.os.Bundle;
80 * import org.xwalk.core.internal.XWalkResourceClientInternal;
81 * import org.xwalk.core.internal.XWalkUIClientInternal;
82 * import org.xwalk.core.internal.XWalkViewInternal;
84 * public class MyActivity extends Activity {
85 * XWalkViewInternal mXwalkView;
87 * class MyResourceClient extends XWalkResourceClientInternal {
88 * MyResourceClient(XWalkViewInternal view) {
93 * WebResourceResponse shouldInterceptLoadRequest(XWalkViewInternal view, String url) {
99 * class MyUIClient extends XWalkUIClientInternal {
100 * MyUIClient(XWalkViewInternal view) {
105 * void onFullscreenToggled(XWalkViewInternal view, String url) {
112 * protected void onCreate(Bundle savedInstanceState) {
113 * mXwalkView = new XWalkViewInternal(this, null);
114 * setContentView(mXwalkView);
115 * mXwalkView.setResourceClient(new MyResourceClient(mXwalkView));
116 * mXwalkView.setUIClient(new MyUIClient(mXwalkView));
117 * mXwalkView.load("http://www.crosswalk-project.org", null);
121 * protected void onActivityResult(int requestCode, int resultCode, Intent data) {
122 * if (mXwalkView != null) {
123 * mXwalkView.onActivityResult(requestCode, resultCode, data);
128 * protected void onNewIntent(Intent intent) {
129 * if (mXwalkView != null) {
130 * mXwalkView.onNewIntent(intent);
136 @XWalkAPI(extendClass = FrameLayout.class, createExternally = true)
137 public class XWalkViewInternal extends android.widget.FrameLayout {
139 private class XWalkActivityStateListener implements ActivityStateListener {
140 WeakReference<XWalkViewInternal> mXWalkViewRef;
142 XWalkActivityStateListener(XWalkViewInternal view) {
143 mXWalkViewRef = new WeakReference<XWalkViewInternal>(view);
147 public void onActivityStateChange(Activity activity, int newState) {
148 XWalkViewInternal view = mXWalkViewRef.get();
149 if (view == null) return;
150 view.onActivityStateChange(activity, newState);
154 static final String PLAYSTORE_DETAIL_URI = "market://details?id=";
155 public static final int INPUT_FILE_REQUEST_CODE = 1;
156 private static final String TAG = XWalkViewInternal.class.getSimpleName();
158 private XWalkContent mContent;
159 private Activity mActivity;
160 private Context mContext;
161 private boolean mIsHidden;
162 private XWalkActivityStateListener mActivityStateListener;
163 private ValueCallback<Uri> mFilePathCallback;
164 private String mCameraPhotoPath;
167 * Normal reload mode as default.
171 public static final int RELOAD_NORMAL = 0;
173 * Reload mode with bypassing the cache.
177 public static final int RELOAD_IGNORE_CACHE = 1;
180 * Constructor for inflating via XML.
181 * @param context a Context object used to access application assets.
182 * @param attrs an AttributeSet passed to our parent.
185 @XWalkAPI(preWrapperLines = {
186 " super(${param1}, ${param2});"},
188 " if (bridge == null) return;",
189 " addView((FrameLayout)bridge, new FrameLayout.LayoutParams(",
190 " FrameLayout.LayoutParams.MATCH_PARENT,",
191 " FrameLayout.LayoutParams.MATCH_PARENT));"})
192 public XWalkViewInternal(Context context, AttributeSet attrs) {
193 super(convertContext(context), attrs);
196 mActivity = (Activity) context;
197 mContext = getContext();
198 init(mContext, attrs);
202 * Constructor for Crosswalk runtime. In shared mode, context isi
203 * different from activity. In embedded mode, they're same.
204 * @param context a Context object used to access application assets
205 * @param activity the activity for this XWalkViewInternal.
208 @XWalkAPI(preWrapperLines = {
209 " super(${param1}, null);"},
211 " if (bridge == null) return;",
212 " addView((FrameLayout)bridge, new FrameLayout.LayoutParams(",
213 " FrameLayout.LayoutParams.MATCH_PARENT,",
214 " FrameLayout.LayoutParams.MATCH_PARENT));"})
215 public XWalkViewInternal(Context context, Activity activity) {
216 super(convertContext(context), null);
219 // Make sure mActivity is initialized before calling 'init' method.
220 mActivity = activity;
221 mContext = getContext();
222 init(mContext, null);
225 private static Context convertContext(Context context) {
226 Context ret = context;
227 Context bridgeContext = ReflectionHelper.getBridgeContext();
228 if (bridgeContext == null || context == null ||
229 bridgeContext.getPackageName().equals(context.getPackageName())) {
230 // Not acrossing package
233 ret = new MixedContext(bridgeContext, context);
239 * Get the current activity passed from callers. It's never null.
240 * @return the activity instance passed from callers.
244 public Activity getActivity() {
245 if (mActivity != null) {
247 } else if (getContext() instanceof Activity) {
248 return (Activity)getContext();
251 // Never achieve here.
256 // TODO(yongsheng): we should remove this since we have getContext()?
260 public Context getViewContext() {
264 public void completeWindowCreation(XWalkViewInternal newXWalkView) {
265 mContent.supplyContentsForPopup(newXWalkView == null ? null : newXWalkView.mContent);
268 private void init(Context context, AttributeSet attrs) {
269 // Initialize chromium resources. Assign them the correct ids in
271 XWalkInternalResources.resetIds(context);
273 // Intialize library, paks and others.
275 XWalkViewDelegate.init(this);
276 mActivityStateListener = new XWalkActivityStateListener(this);
277 ApplicationStatus.registerStateListenerForActivity(
278 mActivityStateListener, getActivity());
279 } catch (Throwable e) {
280 // Try to find if there is UnsatisfiedLinkError in the cause chain of the met Throwable.
281 Throwable linkError = e;
283 if (linkError == null) throw new RuntimeException(e);
284 if (linkError instanceof UnsatisfiedLinkError) break;
285 if (linkError.getCause() == null ||
286 linkError.getCause().equals(linkError)) {
287 throw new RuntimeException(e);
289 linkError = linkError.getCause();
291 final UnsatisfiedLinkError err = (UnsatisfiedLinkError) linkError;
292 final Activity activity = getActivity();
293 final String packageName = context.getPackageName();
294 String missingArch = XWalkViewDelegate.isRunningOnIA() ? "Intel" : "ARM";
295 final String message =
296 context.getString(R.string.cpu_arch_mismatch_message, missingArch);
298 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
299 builder.setTitle(R.string.cpu_arch_mismatch_title)
301 .setOnCancelListener(new DialogInterface.OnCancelListener() {
303 public void onCancel(DialogInterface dialog) {
306 }).setPositiveButton(R.string.goto_store_button_label,
307 new DialogInterface.OnClickListener() {
309 public void onClick(DialogInterface dialog, int which) {
310 activity.startActivity(new Intent(Intent.ACTION_VIEW,
311 Uri.parse(PLAYSTORE_DETAIL_URI + packageName)));
314 }).setNeutralButton(R.string.report_feedback_button_label,
315 new DialogInterface.OnClickListener() {
317 public void onClick(DialogInterface dialog, int which) {
318 ApplicationErrorReport report = new ApplicationErrorReport();
319 report.type = ApplicationErrorReport.TYPE_CRASH;
320 report.packageName = report.processName = packageName;
322 ApplicationErrorReport.CrashInfo crash =
323 new ApplicationErrorReport.CrashInfo();
324 crash.exceptionClassName = err.getClass().getSimpleName();
325 crash.exceptionMessage = "CPU architecture mismatch";
326 StringWriter writer = new StringWriter();
327 PrintWriter print = new PrintWriter(writer);
328 err.printStackTrace(print);
329 crash.stackTrace = writer.toString();
330 StackTraceElement stack = err.getStackTrace()[0];
331 crash.throwClassName = stack.getClassName();
332 crash.throwFileName = stack.getFileName();
333 crash.throwLineNumber = stack.getLineNumber();
334 crash.throwMethodName = stack.getMethodName();
336 report.crashInfo = crash;
337 report.systemApp = false;
338 report.time = System.currentTimeMillis();
340 Intent intent = new Intent(Intent.ACTION_APP_ERROR);
341 intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
342 activity.startActivity(intent);
346 builder.create().show();
350 initXWalkContent(context, attrs);
353 private void initXWalkContent(Context context, AttributeSet attrs) {
355 mContent = new XWalkContent(context, attrs, this);
357 new FrameLayout.LayoutParams(
358 FrameLayout.LayoutParams.MATCH_PARENT,
359 FrameLayout.LayoutParams.MATCH_PARENT));
362 // Set default XWalkClientImpl.
363 setXWalkClient(new XWalkClient(this));
364 // Set default XWalkWebChromeClient and DownloadListener. The default actions
365 // are provided via the following clients if special actions are not needed.
366 setXWalkWebChromeClient(new XWalkWebChromeClient(this));
368 // Set with internal implementation. Could be overwritten by embedders'
370 setUIClient(new XWalkUIClientInternal(this));
371 setResourceClient(new XWalkResourceClientInternal(this));
373 setDownloadListener(new XWalkDownloadListenerImpl(context));
374 setNavigationHandler(new XWalkNavigationHandlerImpl(context));
375 setNotificationService(new XWalkNotificationServiceImpl(context, this));
377 if (!CommandLine.getInstance().hasSwitch("disable-xwalk-extensions")) {
378 BuiltinXWalkExtensions.load(context, getActivity());
380 XWalkPreferencesInternal.setValue(XWalkPreferencesInternal.ENABLE_EXTENSIONS, false);
383 XWalkPathHelper.initialize();
384 XWalkPathHelper.setCacheDirectory(
385 mContext.getApplicationContext().getCacheDir().getPath());
387 String state = Environment.getExternalStorageState();
388 if (Environment.MEDIA_MOUNTED.equals(state) ||
389 Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
390 File extCacheDir = mContext.getApplicationContext().getExternalCacheDir();
391 if (null != extCacheDir) {
392 XWalkPathHelper.setExternalCacheDirectory(extCacheDir.getPath());
398 * Load a web page/app from a given base URL or a content.
399 * If url is null or empty and content is null or empty, then this function
401 * If content is not null, load the web page/app from the content.
402 * If content is not null and the url is not set, return "about:blank" ifi
403 * calling {@link XWalkViewInternal#getUrl()}.
404 * If content is null, try to load the content from the url.
406 * It supports URL schemes like 'http:', 'https:' and 'file:'.
407 * It can also load files from Android assets, e.g. 'file:///android_asset/'.
408 * @param url the url for web page/app.
409 * @param content the content for the web page/app. Could be empty.
413 public void load(String url, String content) {
414 if (mContent == null) return;
416 mContent.loadUrl(url, content);
420 * Load a web app from a given manifest.json file. If content is not null,
421 * load the manifest.json from the content. If content is null, try to load
422 * the manifest.json from the url. Note that url should not be null if the
423 * launched path defined in manifest.json is relative.
425 * It supports URL schemes like 'http:', 'https:' and 'file:'.
426 * It can also load files from Android assets, e.g. 'file:///android_asset/'.
427 * @param url the url for manifest.json.
428 * @param content the content for manifest.json.
432 public void loadAppFromManifest(String url, String content) {
433 if (mContent == null) return;
435 mContent.loadAppFromManifest(url, content);
439 * Reload a web app with a given mode.
440 * @param mode the reload mode.
444 public void reload(int mode) {
445 if (mContent == null) return;
447 mContent.reload(mode);
451 * Stop current loading progress.
455 public void stopLoading() {
456 if (mContent == null) return;
458 mContent.stopLoading();
462 * Get the url of current web page/app. This may be different from what's passed
464 * @return the url for current web page/app.
468 public String getUrl() {
469 if (mContent == null) return null;
471 return mContent.getUrl();
475 * Get the title of current web page/app. This may be different from what's passed
477 * @return the title for current web page/app.
481 public String getTitle() {
482 if (mContent == null) return null;
484 return mContent.getTitle();
488 * Get the original url specified by caller.
489 * @return the original url.
493 public String getOriginalUrl() {
494 if (mContent == null) return null;
496 return mContent.getOriginalUrl();
500 * Get the navigation history for current XWalkViewInternal. It's synchronized with
501 * this XWalkViewInternal if any backward/forward and navigation operations.
502 * @return the navigation history.
506 public XWalkNavigationHistoryInternal getNavigationHistory() {
507 if (mContent == null) return null;
509 return mContent.getNavigationHistory();
513 * Injects the supplied Java object into this XWalkViewInternal.
514 * Each method defined in the class of the object should be
515 * marked with {@link JavascriptInterface} if it's called by JavaScript.
516 * @param object the supplied Java object, called by JavaScript.
517 * @param name the name injected in JavaScript.
521 public void addJavascriptInterface(Object object, String name) {
522 if (mContent == null) return;
524 mContent.addJavascriptInterface(object, name);
528 * Evaluate a fragment of JavaScript code and get the result via callback.
529 * @param script the JavaScript string.
530 * @param callback the callback to handle the evaluated result.
534 public void evaluateJavascript(String script, ValueCallback<String> callback) {
535 if (mContent == null) return;
537 mContent.evaluateJavascript(script, callback);
541 * Clear the resource cache. Note that the cache is per-application, so this
542 * will clear the cache for all XWalkViews used.
543 * @param includeDiskFiles indicate whether to clear disk files for cache.
547 public void clearCache(boolean includeDiskFiles) {
548 if (mContent == null) return;
550 mContent.clearCache(includeDiskFiles);
554 * Indicate that a HTML element is occupying the whole screen.
555 * @return true if any HTML element is occupying the whole screen.
559 public boolean hasEnteredFullscreen() {
560 if (mContent == null) return false;
562 return mContent.hasEnteredFullscreen();
566 * Leave fullscreen mode if it's. Do nothing if it's not
571 public void leaveFullscreen() {
572 if (mContent == null) return;
574 mContent.exitFullscreen();
578 * Pause all layout, parsing and JavaScript timers for all XWalkViewInternal instances.
579 * It will be called when the container Activity get paused. It can also be explicitly
580 * called to pause timers.
582 * Note that it will globally impact all XWalkViewInternal instances, not limited to
583 * just this XWalkViewInternal.
588 public void pauseTimers() {
589 if (mContent == null) return;
591 mContent.pauseTimers();
595 * Resume all layout, parsing and JavaScript timers for all XWalkViewInternal instances.
596 * It will be called when the container Activity get resumed. It can also be explicitly
597 * called to resume timers.
599 * Note that it will globally impact all XWalkViewInternal instances, not limited to
600 * just this XWalkViewInternal.
605 public void resumeTimers() {
606 if (mContent == null) return;
608 mContent.resumeTimers();
612 * Pause many other things except JavaScript timers inside rendering engine,
613 * like video player, modal dialogs, etc. See {@link #pauseTimers} about pausing
615 * It will be called when the container Activity get paused. It can also be explicitly
616 * called to pause above things.
620 public void onHide() {
621 if (mContent == null || mIsHidden) return;
627 * Resume video player, modal dialogs. Embedders are in charge of calling
628 * this during resuming this activity if they call onHide.
629 * Typically it should be called when the activity for this view is resumed.
630 * It will be called when the container Activity get resumed. It can also be explicitly
631 * called to resume above things.
635 public void onShow() {
636 if (mContent == null || !mIsHidden ) return;
642 * Release internal resources occupied by this XWalkViewInternal.
643 * It will be called when the container Activity get destroyed. It can also be explicitly
644 * called to release resources.
648 public void onDestroy() {
653 * Pass through activity result to XWalkViewInternal. Many internal facilities need this
654 * to handle activity result like JavaScript dialog, Crosswalk extensions, etc.
655 * See <a href="http://developer.android.com/reference/android/app/Activity.html">
656 * android.app.Activity.onActivityResult()</a>.
657 * @param requestCode passed from android.app.Activity.onActivityResult().
658 * @param resultCode passed from android.app.Activity.onActivityResult().
659 * @param data passed from android.app.Activity.onActivityResult().
663 public void onActivityResult(int requestCode, int resultCode, Intent data) {
664 if (mContent == null) return;
665 if(requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
668 // Check that the response is a good one
669 if(Activity.RESULT_OK == resultCode) {
671 // If there is not data, then we may have taken a photo
672 if(mCameraPhotoPath != null) {
673 results = Uri.parse(mCameraPhotoPath);
676 String dataString = data.getDataString();
677 if (dataString != null) {
678 results = Uri.parse(dataString);
683 mFilePathCallback.onReceiveValue(results);
684 mFilePathCallback = null;
687 mContent.onActivityResult(requestCode, resultCode, data);
691 * Pass through intents to XWalkViewInternal. Many internal facilities need this
692 * to receive the intents like web notification. See
693 * <a href="http://developer.android.com/reference/android/app/Activity.html">
694 * android.app.Activity.onNewIntent()</a>.
695 * @param intent passed from android.app.Activity.onNewIntent().
699 public boolean onNewIntent(Intent intent) {
700 if (mContent == null) return false;
701 return mContent.onNewIntent(intent);
705 * Save current internal state of this XWalkViewInternal. This can help restore this state
706 * afterwards restoring.
707 * @param outState the saved state for restoring.
711 public boolean saveState(Bundle outState) {
712 if (mContent == null) return false;
713 mContent.saveState(outState);
718 * Restore the state from the saved bundle data.
719 * @param inState the state saved from saveState().
720 * @return true if it can restore the state.
724 public boolean restoreState(Bundle inState) {
725 if (mContent == null) return false;
726 if (mContent.restoreState(inState) != null) return true;
731 * Get the API version of Crosswalk embedding API.
732 * @return the string of API level.
735 // TODO(yongsheng): make it static?
737 public String getAPIVersion() {
742 * Get the Crosswalk version.
743 * @return the string of Crosswalk.
746 // TODO(yongsheng): make it static?
748 public String getXWalkVersion() {
749 if (mContent == null) return null;
750 return mContent.getXWalkVersion();
754 * Embedders use this to customize their handlers to events/callbacks related
756 * @param client the XWalkUIClientInternal defined by callers.
760 public void setUIClient(XWalkUIClientInternal client) {
761 if (mContent == null) return;
763 mContent.setUIClient(client);
767 * Embedders use this to customize their handlers to events/callbacks related
768 * to resource loading.
769 * @param client the XWalkResourceClientInternal defined by callers.
773 public void setResourceClient(XWalkResourceClientInternal client) {
774 if (mContent == null) return;
776 mContent.setResourceClient(client);
780 * Set Background color of the view
784 public void setBackgroundColor(int color) {
785 if (mContent == null) return;
787 mContent.setBackgroundColor(color);
791 * override setLayerType
795 public void setLayerType(int layerType, Paint paint) {
796 if (layerType != LAYER_TYPE_SOFTWARE) {
797 super.setLayerType(layerType, paint);
799 Log.w(TAG, "LAYER_TYPE_SOFTWARE is not supported by XwalkView");
803 // TODO(yongsheng): this is not public.
807 public XWalkSettings getSettings() {
808 if (mContent == null) return null;
810 return mContent.getSettings();
814 * This method is used by Cordova for hacking.
815 * TODO(yongsheng): remove this and related test cases?
820 public void setNetworkAvailable(boolean networkUp) {
821 if (mContent == null) return;
823 mContent.setNetworkAvailable(networkUp);
827 * Enables remote debugging and returns the URL at which the dev tools
828 * server is listening for commands.
830 public void enableRemoteDebugging() {
831 if (mContent == null) return;
833 mContent.enableRemoteDebugging();
837 * Get the websocket url for remote debugging.
838 * @return the web socket url to remote debug this xwalk view.
839 * null will be returned if remote debugging is not enabled.
843 public Uri getRemoteDebuggingUrl() {
844 if (mContent == null) return null;
846 String wsUrl = mContent.getRemoteDebuggingUrl();
847 if (wsUrl == null || wsUrl.isEmpty()) return null;
849 return Uri.parse(wsUrl);
853 * It's used for Presentation API.
856 public int getContentID() {
857 if (mContent == null) return -1;
858 return mContent.getRoutingID();
861 boolean canGoBack() {
862 if (mContent == null) return false;
864 return mContent.canGoBack();
868 if (mContent == null) return;
873 boolean canGoForward() {
874 if (mContent == null) return false;
876 return mContent.canGoForward();
880 if (mContent == null) return;
882 mContent.goForward();
885 void clearHistory() {
886 if (mContent == null) return;
888 mContent.clearHistory();
892 if (mContent == null) return;
893 ApplicationStatus.unregisterActivityStateListener(mActivityStateListener);
894 mActivityStateListener = null;
896 disableRemoteDebugging();
899 void disableRemoteDebugging() {
900 if (mContent == null) return;
902 mContent.disableRemoteDebugging();
905 private static void checkThreadSafety() {
906 if (Looper.myLooper() != Looper.getMainLooper()) {
907 Throwable throwable = new Throwable(
908 "Warning: A XWalkViewInternal method was called on thread '" +
909 Thread.currentThread().getName() + "'. " +
910 "All XWalkViewInternal methods must be called on the UI thread. ");
911 throw new RuntimeException(throwable);
915 boolean isOwnerActivityRunning() {
916 int status = ApplicationStatus.getStateForActivity(getActivity());
917 if (status == ActivityState.DESTROYED) return false;
921 void navigateTo(int offset) {
922 if (mContent == null) return;
923 mContent.navigateTo(offset);
926 void setOverlayVideoMode(boolean enabled) {
927 mContent.setOverlayVideoMode(enabled);
930 // Below methods are for test shell and instrumentation tests.
934 public void setXWalkClient(XWalkClient client) {
935 if (mContent == null) return;
937 mContent.setXWalkClient(client);
943 public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
944 if (mContent == null) return;
946 mContent.setXWalkWebChromeClient(client);
952 public void setDownloadListener(DownloadListener listener) {
953 if (mContent == null) return;
955 mContent.setDownloadListener(listener);
961 public void setNavigationHandler(XWalkNavigationHandler handler) {
962 if (mContent == null) return;
964 mContent.setNavigationHandler(handler);
970 public void setNotificationService(XWalkNotificationService service) {
971 if (mContent == null) return;
973 mContent.setNotificationService(service);
980 public boolean dispatchKeyEvent(KeyEvent event) {
981 if (event.getAction() == KeyEvent.ACTION_UP &&
982 event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
983 // If there's navigation happens when app is fullscreen,
984 // the content will still be fullscreen after navigation.
985 // In such case, the back key will exit fullscreen first.
986 if (hasEnteredFullscreen()) {
989 } else if (canGoBack()) {
994 return super.dispatchKeyEvent(event);
997 private void onActivityStateChange(Activity activity, int newState) {
998 assert(getActivity() == activity);
1000 case ActivityState.STARTED:
1003 case ActivityState.PAUSED:
1006 case ActivityState.RESUMED:
1009 case ActivityState.DESTROYED:
1012 case ActivityState.STOPPED:
1021 * Tell the client to show a file chooser.
1022 * @param uploadFile the callback class to handle the result from caller. It MUST
1023 * be invoked in all cases. Leave it not invoked will block all following
1024 * requests to open file chooser.
1025 * @param acceptType value of the 'accept' attribute of the input tag associated
1026 * with this file picker.
1027 * @param capture value of the 'capture' attribute of the input tag associated
1028 * with this file picker
1030 public boolean showFileChooser(ValueCallback<Uri> uploadFile, String acceptType,
1032 mFilePathCallback = uploadFile;
1034 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
1035 if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
1036 // Create the File where the photo should go
1037 File photoFile = null;
1039 photoFile = createImageFile();
1040 takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
1041 } catch (IOException ex) {
1042 // Error occurred while creating the File
1043 Log.e(TAG, "Unable to create Image File", ex);
1046 // Continue only if the File was successfully created
1047 if (photoFile != null) {
1048 mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
1049 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
1050 Uri.fromFile(photoFile));
1052 takePictureIntent = null;
1056 Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
1057 contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
1058 contentSelectionIntent.setType("*/*");
1060 Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
1061 Intent soundRecorder = new Intent(
1062 MediaStore.Audio.Media.RECORD_SOUND_ACTION);
1063 ArrayList<Intent> extraIntents = new ArrayList<Intent>();
1064 extraIntents.add(takePictureIntent);
1065 extraIntents.add(camcorder);
1066 extraIntents.add(soundRecorder);
1068 Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
1069 chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
1070 chooserIntent.putExtra(Intent.EXTRA_TITLE, "Choose an action");
1071 chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
1072 extraIntents.toArray(new Intent[] { }));
1073 getActivity().startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
1077 private File createImageFile() throws IOException {
1078 // Create an image file name
1079 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
1080 String imageFileName = "JPEG_" + timeStamp + "_";
1081 File storageDir = Environment.getExternalStoragePublicDirectory(
1082 Environment.DIRECTORY_PICTURES);
1083 File imageFile = File.createTempFile(
1084 imageFileName, /* prefix */
1085 ".jpg", /* suffix */
1086 storageDir /* directory */