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;
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.Rect;
14 import android.net.Uri;
15 import android.os.Bundle;
16 import android.os.Looper;
17 import android.util.AttributeSet;
18 import android.view.KeyEvent;
19 import android.view.ViewGroup;
20 import android.webkit.ValueCallback;
21 import android.widget.FrameLayout;
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
26 import org.chromium.base.ActivityState;
27 import org.chromium.base.ApplicationStatus;
29 import org.xwalk.core.extension.XWalkExtensionManager;
32 * XWalkView represents an Android view for web apps/pages. Thus most of attributes
33 * for Android view are true for this class. It includes an instance of
34 * android.view.SurfaceView for rendering. Currently limitations for android.view.SurfaceView
35 * also are applied for this class as well, like resizing, retation, transformation and
38 * It provides 2 major callback classes, namely XWalkResourceClient and XWalkUIClient for
39 * listening to the events related resource loading and UI. By default, Crosswalk has an inner
40 * implementation. Callers can override them if like.
42 * Unlike other Android views, this class has to listen to system events like application life
43 * cycle, intents, and activity result. The web engine inside this view need to handle them.
45 * It already includes all newly created Web APIs from Crosswalk like Presentation,
46 * DeviceCapabilities, etc..
48 public class XWalkView extends android.widget.FrameLayout {
50 protected static final String PLAYSTORE_DETAIL_URI = "market://details?id=";
52 private XWalkContent mContent;
53 private Activity mActivity;
54 private Context mContext;
55 private XWalkExtensionManager mExtensionManager;
57 /** Normal reload mode as default. */
58 public static final int RELOAD_NORMAL = 0;
59 /** Reload mode with bypassing the cache. */
60 public static final int RELOAD_IGNORE_CACHE = 1;
63 * Constructor for inflating via XML.
64 * @param context a Context object used to access application assets.
65 * @param attrs an AttributeSet passed to our parent.
67 public XWalkView(Context context, AttributeSet attrs) {
68 super(context, attrs);
76 * Constructor for Crosswalk runtime. In shared mode, context isi
77 * different from activity. In embedded mode, they're same.
78 * @param context a Context object used to access application assets
79 * @param activity the activity for this XWalkView.
81 public XWalkView(Context context, Activity activity) {
85 // Make sure mActivity is initialized before calling 'init' method.
92 * Get the current activity passed from callers. It's never null.
93 * @return the activity instance passed from callers.
97 public Activity getActivity() {
98 if (mActivity != null) {
100 } else if (getContext() instanceof Activity) {
101 return (Activity)getContext();
104 // Never achieve here.
109 // TODO(yongsheng): we should remove this since we have getContext()?
113 public Context getViewContext() {
117 private void init(Context context, AttributeSet attrs) {
118 // Initialize chromium resources. Assign them the correct ids in
120 XWalkInternalResources.resetIds(context);
122 // Intialize library, paks and others.
124 XWalkViewDelegate.init(this);
125 } catch (UnsatisfiedLinkError e) {
126 final UnsatisfiedLinkError err = e;
127 final Activity activity = getActivity();
128 final String packageName = context.getPackageName();
129 String missingArch = XWalkViewDelegate.isRunningOnIA() ? "Intel" : "ARM";
130 final String message =
131 context.getString(R.string.cpu_arch_mismatch_message, missingArch);
133 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
134 builder.setTitle(R.string.cpu_arch_mismatch_title)
136 .setOnCancelListener(new DialogInterface.OnCancelListener() {
138 public void onCancel(DialogInterface dialog) {
141 }).setPositiveButton(R.string.goto_store_button_label,
142 new DialogInterface.OnClickListener() {
144 public void onClick(DialogInterface dialog, int which) {
145 activity.startActivity(new Intent(Intent.ACTION_VIEW,
146 Uri.parse(PLAYSTORE_DETAIL_URI + packageName)));
149 }).setNeutralButton(R.string.report_feedback_button_label,
150 new DialogInterface.OnClickListener() {
152 public void onClick(DialogInterface dialog, int which) {
153 ApplicationErrorReport report = new ApplicationErrorReport();
154 report.type = ApplicationErrorReport.TYPE_CRASH;
155 report.packageName = report.processName = packageName;
157 ApplicationErrorReport.CrashInfo crash =
158 new ApplicationErrorReport.CrashInfo();
159 crash.exceptionClassName = err.getClass().getSimpleName();
160 crash.exceptionMessage = "CPU architecture mismatch";
161 StringWriter writer = new StringWriter();
162 PrintWriter print = new PrintWriter(writer);
163 err.printStackTrace(print);
164 crash.stackTrace = writer.toString();
165 StackTraceElement stack = err.getStackTrace()[0];
166 crash.throwClassName = stack.getClassName();
167 crash.throwFileName = stack.getFileName();
168 crash.throwLineNumber = stack.getLineNumber();
169 crash.throwMethodName = stack.getMethodName();
171 report.crashInfo = crash;
172 report.systemApp = false;
173 report.time = System.currentTimeMillis();
175 Intent intent = new Intent(Intent.ACTION_APP_ERROR);
176 intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
177 activity.startActivity(intent);
181 builder.create().show();
185 initXWalkContent(context, attrs);
188 private void initXWalkContent(Context context, AttributeSet attrs) {
189 mContent = new XWalkContent(context, attrs, this);
191 new FrameLayout.LayoutParams(
192 FrameLayout.LayoutParams.MATCH_PARENT,
193 FrameLayout.LayoutParams.MATCH_PARENT));
196 // Set default XWalkClientImpl.
197 setXWalkClient(new XWalkClient(this));
198 // Set default XWalkWebChromeClient and DownloadListener. The default actions
199 // are provided via the following clients if special actions are not needed.
200 setXWalkWebChromeClient(new XWalkWebChromeClient(this));
202 // Set with internal implementation. Could be overwritten by embedders'
204 setUIClient(new XWalkUIClient(this));
205 setResourceClient(new XWalkResourceClient(this));
207 setDownloadListener(new XWalkDownloadListenerImpl(context));
208 setNavigationHandler(new XWalkNavigationHandlerImpl(context));
209 setNotificationService(new XWalkNotificationServiceImpl(context, this));
211 // Enable xwalk extension mechanism and start load extensions here.
212 // Note that it has to be after above initialization.
213 mExtensionManager = new XWalkExtensionManager(context, getActivity());
214 mExtensionManager.loadExtensions();
218 * Load a web page/app from a given base URL or a content.
219 * If url is null or empty and content is null or empty, then this function
221 * If content is not null, load the web page/app from the content.
222 * If content is not null and the url is not set, return "about:blank" ifi
223 * calling {@link XWalkView#getUrl()}.
224 * If content is null, try to load the content from the url.
226 * It supports URL schemes like 'http:', 'https:' and 'file:'.
227 * It can also load files from Android assets, e.g. 'file:///android_asset/'.
228 * @param url the url for web page/app.
229 * @param content the content for the web page/app. Could be empty.
231 public void load(String url, String content) {
232 if (mContent == null) return;
234 mContent.loadUrl(url, content);
238 * Load a web app from a given manifest.json file. If content is not null,
239 * load the manifest.json from the content. If content is null, try to load
240 * the manifest.json from the url. Note that url should not be null if the
241 * launched path defined in manifest.json is relative.
243 * It supports URL schemes like 'http:', 'https:' and 'file:'.
244 * It can also load files from Android assets, e.g. 'file:///android_asset/'.
245 * @param url the url for manifest.json.
246 * @param content the content for manifest.json.
248 public void loadAppFromManifest(String url, String content) {
249 if (mContent == null) return;
251 mContent.loadAppFromManifest(url, content);
255 * Reload a web app with a given mode.
256 * @param mode the reload mode.
258 public void reload(int mode) {
259 if (mContent == null) return;
261 mContent.reload(mode);
265 * Stop current loading progress.
267 public void stopLoading() {
268 if (mContent == null) return;
270 mContent.stopLoading();
274 * Get the url of current web page/app. This may be different from what's passed
276 * @return the url for current web page/app.
278 public String getUrl() {
279 if (mContent == null) return null;
281 return mContent.getUrl();
285 * Get the title of current web page/app. This may be different from what's passed
287 * @return the title for current web page/app.
289 public String getTitle() {
290 if (mContent == null) return null;
292 return mContent.getTitle();
296 * Get the original url specified by caller.
297 * @return the original url.
299 public String getOriginalUrl() {
300 if (mContent == null) return null;
302 return mContent.getOriginalUrl();
306 * Get the navigation history for current XWalkView. It's synchronized with
307 * this XWalkView if any backward/forward and navigation operations.
308 * @return the navigation history.
310 public XWalkNavigationHistory getNavigationHistory() {
311 if (mContent == null) return null;
313 return mContent.getNavigationHistory();
317 * Injects the supplied Java object into this XWalkView.
318 * Each method defined in the class of the object should be
319 * marked with {@link JavascriptInterface} if it's called by JavaScript.
320 * @param object the supplied Java object, called by JavaScript.
321 * @param name the name injected in JavaScript.
323 public void addJavascriptInterface(Object object, String name) {
324 if (mContent == null) return;
326 mContent.addJavascriptInterface(object, name);
330 * Evaluate a fragment of JavaScript code and get the result via callback.
331 * @param script the JavaScript string.
332 * @param callback the callback to handle the evaluated result.
334 public void evaluateJavascript(String script, ValueCallback<String> callback) {
335 if (mContent == null) return;
337 mContent.evaluateJavascript(script, callback);
341 * Clear the resource cache. Note that the cache is per-application, so this
342 * will clear the cache for all XWalkViews used.
343 * @param includeDiskFiles indicate whether to clear disk files for cache.
345 public void clearCache(boolean includeDiskFiles) {
346 if (mContent == null) return;
348 mContent.clearCache(includeDiskFiles);
352 * Indicate that a HTML element is occupying the whole screen.
353 * @return true if any HTML element is occupying the whole screen.
355 public boolean hasEnteredFullscreen() {
356 if (mContent == null) return false;
358 return mContent.hasEnteredFullscreen();
362 * Leave fullscreen mode if it's. Do nothing if it's not
365 public void leaveFullscreen() {
366 if (mContent == null) return;
368 mContent.exitFullscreen();
372 * Pause timers of rendering engine. Typically it should be called
373 * when the activity for this view is paused.
375 public void pauseTimers() {
376 if (mContent == null) return;
378 mContent.pauseTimers();
382 * Resume timers of rendering engine. Typically it should be called
383 * when the activyt for this view is resumed.
385 public void resumeTimers() {
386 if (mContent == null) return;
388 mContent.resumeTimers();
392 * Aside from timers, this method can pause many other things inside
393 * rendering engine, like video player, modal dialogs, etc.
394 * Typically it should be called when the activity for this view is paused.
396 public void onHide() {
397 if (mContent == null) return;
398 mExtensionManager.onPause();
403 * Resume video player, modal dialogs. Embedders are in charge of calling
404 * this during resuming this activity if they call onHide.
405 * Typically it should be called when the activity for this view is resumed.
407 public void onShow() {
408 if (mContent == null) return;
409 mExtensionManager.onResume();
414 * Release internal resources occupied by this XWalkView.
416 public void onDestroy() {
421 * Pass through activity result to XWalkView. Many internal facilities need this
422 * to handle activity result like JavaScript dialog, Crosswalk extensions, etc.
423 * See android.app.Activity.onActivityResult().
424 * @param requestCode passed from android.app.Activity.onActivityResult().
425 * @param resultCode passed from android.app.Activity.onActivityResult().
426 * @param data passed from android.app.Activity.onActivityResult().
428 public void onActivityResult(int requestCode, int resultCode, Intent data) {
429 if (mContent == null) return;
430 mExtensionManager.onActivityResult(requestCode, resultCode, data);
431 mContent.onActivityResult(requestCode, resultCode, data);
435 * Pass through intents to XWalkView. Many internal facilities need this
436 * to receive the intents like web notification. See
437 * android.app.Activity.onNewIntent().
438 * @param intent passed from android.app.Activity.onNewIntent().
440 public boolean onNewIntent(Intent intent) {
441 if (mContent == null) return false;
442 return mContent.onNewIntent(intent);
446 * Save current internal state of this XWalkView. This can help restore this state
447 * afterwards restoring.
448 * @param outState the saved state for restoring.
450 public boolean saveState(Bundle outState) {
451 if (mContent == null) return false;
452 mContent.saveState(outState);
457 * Restore the state from the saved bundle data.
458 * @param inState the state saved from saveState().
459 * @return true if it can restore the state.
461 public boolean restoreState(Bundle inState) {
462 if (mContent == null) return false;
463 if (mContent.restoreState(inState) != null) return true;
468 * Get the API version of Crosswalk embedding API.
469 * @return the string of API level.
471 // TODO(yongsheng): make it static?
472 public String getAPIVersion() {
477 * Get the Crosswalk version.
478 * @return the string of Crosswalk.
480 // TODO(yongsheng): make it static?
481 public String getXWalkVersion() {
482 if (mContent == null) return null;
483 return mContent.getXWalkVersion();
487 * Embedders use this to customize their handlers to events/callbacks related
489 * @param client the XWalkUIClient defined by callers.
491 public void setUIClient(XWalkUIClient client) {
492 if (mContent == null) return;
494 mContent.setUIClient(client);
498 * Embedders use this to customize their handlers to events/callbacks related
499 * to resource loading.
500 * @param client the XWalkResourceClient defined by callers.
502 public void setResourceClient(XWalkResourceClient client) {
503 if (mContent == null) return;
505 mContent.setResourceClient(client);
509 * Inherit from android.view.View. This class needs to handle some keys like
511 * @param keyCode passed from android.view.View.onKeyUp().
512 * @param event passed from android.view.View.onKeyUp().
515 public boolean onKeyUp(int keyCode, KeyEvent event) {
516 if (keyCode == KeyEvent.KEYCODE_BACK) {
517 // If there's navigation happens when app is fullscreen,
518 // the content will still be fullscreen after navigation.
519 // In such case, the back key will exit fullscreen first.
520 if (hasEnteredFullscreen()) {
523 } else if (canGoBack()) {
531 // TODO(yongsheng): this is not public.
535 public XWalkSettings getSettings() {
536 if (mContent == null) return null;
538 return mContent.getSettings();
542 * This method is used by Cordova for hacking.
543 * TODO(yongsheng): remove this and related test cases?
547 public void setNetworkAvailable(boolean networkUp) {
548 if (mContent == null) return;
550 mContent.setNetworkAvailable(networkUp);
554 * Enables remote debugging and returns the URL at which the dev tools server is listening
555 * for commands. The allowedUid argument can be used to specify the uid of the process that is
556 * permitted to connect.
557 * TODO(yongsheng): how to enable this in XWalkPreferences?
561 public String enableRemoteDebugging(int allowedUid) {
562 if (mContent == null) return null;
564 return mContent.enableRemoteDebugging(allowedUid);
568 * It's used for Presentation API.
571 public int getContentID() {
572 if (mContent == null) return -1;
573 return mContent.getRoutingID();
576 boolean canGoBack() {
577 if (mContent == null) return false;
579 return mContent.canGoBack();
583 if (mContent == null) return;
588 boolean canGoForward() {
589 if (mContent == null) return false;
591 return mContent.canGoForward();
595 if (mContent == null) return;
597 mContent.goForward();
600 void clearHistory() {
601 if (mContent == null) return;
603 mContent.clearHistory();
607 if (mContent == null) return;
608 mExtensionManager.onDestroy();
610 disableRemoteDebugging();
613 // Enables remote debugging and returns the URL at which the dev tools server is listening
614 // for commands. Only the current process is allowed to connect to the server.
615 String enableRemoteDebugging() {
616 return enableRemoteDebugging(mContext.getApplicationInfo().uid);
619 void disableRemoteDebugging() {
620 if (mContent == null) return;
622 mContent.disableRemoteDebugging();
625 private static void checkThreadSafety() {
626 if (Looper.myLooper() != Looper.getMainLooper()) {
627 Throwable throwable = new Throwable(
628 "Warning: A XWalkView method was called on thread '" +
629 Thread.currentThread().getName() + "'. " +
630 "All XWalkView methods must be called on the UI thread. ");
631 throw new RuntimeException(throwable);
635 boolean isOwnerActivityRunning() {
636 int status = ApplicationStatus.getStateForActivity(getActivity());
637 if (status == ActivityState.DESTROYED) return false;
641 void navigateTo(int offset) {
642 if (mContent == null) return;
643 mContent.navigateTo(offset);
646 void setOverlayVideoMode(boolean enabled) {
647 mContent.setOverlayVideoMode(enabled);
650 // Below methods are for test shell and instrumentation tests.
654 public void setXWalkClient(XWalkClient client) {
655 if (mContent == null) return;
657 mContent.setXWalkClient(client);
663 public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
664 if (mContent == null) return;
666 mContent.setXWalkWebChromeClient(client);
672 public void setDownloadListener(DownloadListener listener) {
673 if (mContent == null) return;
675 mContent.setDownloadListener(listener);
681 public void setNavigationHandler(XWalkNavigationHandler handler) {
682 if (mContent == null) return;
684 mContent.setNavigationHandler(handler);
690 public void setNotificationService(XWalkNotificationService service) {
691 if (mContent == null) return;
693 mContent.setNotificationService(service);