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.Rect;
14 import android.net.Uri;
15 import android.os.Bundle;
16 import android.os.Environment;
17 import android.os.Looper;
18 import android.util.AttributeSet;
19 import android.view.KeyEvent;
20 import android.view.ViewGroup;
21 import android.webkit.ValueCallback;
22 import android.widget.FrameLayout;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.lang.ref.WeakReference;
29 import org.chromium.base.ActivityState;
30 import org.chromium.base.ApplicationStatus;
31 import org.chromium.base.ApplicationStatus.ActivityStateListener;
32 import org.chromium.base.CommandLine;
33 import org.xwalk.core.internal.extension.BuiltinXWalkExtensions;
36 * <p>XWalkViewInternal represents an Android view for web apps/pages. Thus most of attributes
37 * for Android view are valid for this class. Since it internally uses
38 * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
39 * android.view.SurfaceView</a> for rendering web pages by default, it can't be resized,
40 * rotated, transformed and animated due to the limitations of SurfaceView.
41 * Alternatively, if the preference key {@link XWalkPreferencesInternal#ANIMATABLE_XWALK_VIEW}
42 * is set to True, XWalkViewInternal can be transformed and animated because
43 * <a href="http://developer.android.com/reference/android/view/TextureView.html">
44 * TextureView</a> is intentionally used to render web pages for animation support.
45 * Besides, XWalkViewInternal won't be rendered if it's invisible.</p>
47 * <p>XWalkViewInternal needs hardware acceleration to render web pages. As a result, the
48 * AndroidManifest.xml of the caller's app must be appended with the attribute
49 * "android:hardwareAccelerated" and its value must be set as "true".</p>
51 * <application android:name="android.app.Application" android:label="XWalkUsers"
52 * android:hardwareAccelerated="true">
55 * <p>Crosswalk provides 2 major callback classes, namely {@link XWalkResourceClientInternal} and
56 * {@link XWalkUIClientInternal} for listening to the events related to resource loading and UI.
57 * By default, Crosswalk has a default implementation. Callers can override them if needed.</p>
59 * <p>Unlike other Android views, this class has to listen to system events like intents and activity result.
60 * The web engine inside this view need to get and handle them.
61 * With contianer activity's lifecycle change, XWalkViewInternal will pause all timers and other
62 * components like videos when activity paused, resume back them when activity resumed.
63 * When activity is about to destroy, XWalkViewInternal will destroy itself as well.
64 * Embedders can also call onHide() and pauseTimers() to explicitly pause XWalkViewInternal.
65 * Similarily with onShow(), resumeTimers() and onDestroy().
70 * import android.app.Activity;
71 * import android.os.Bundle;
73 * import org.xwalk.core.internal.XWalkResourceClientInternal;
74 * import org.xwalk.core.internal.XWalkUIClientInternal;
75 * import org.xwalk.core.internal.XWalkViewInternal;
77 * public class MyActivity extends Activity {
78 * XWalkViewInternal mXwalkView;
80 * class MyResourceClient extends XWalkResourceClientInternal {
81 * MyResourceClient(XWalkViewInternal view) {
86 * WebResourceResponse shouldInterceptLoadRequest(XWalkViewInternal view, String url) {
92 * class MyUIClient extends XWalkUIClientInternal {
93 * MyUIClient(XWalkViewInternal view) {
98 * void onFullscreenToggled(XWalkViewInternal view, String url) {
105 * protected void onCreate(Bundle savedInstanceState) {
106 * mXwalkView = new XWalkViewInternal(this, null);
107 * setContentView(mXwalkView);
108 * mXwalkView.setResourceClient(new MyResourceClient(mXwalkView));
109 * mXwalkView.setUIClient(new MyUIClient(mXwalkView));
110 * mXwalkView.load("http://www.crosswalk-project.org", null);
114 * protected void onActivityResult(int requestCode, int resultCode, Intent data) {
115 * if (mXwalkView != null) {
116 * mXwalkView.onActivityResult(requestCode, resultCode, data);
121 * protected void onNewIntent(Intent intent) {
122 * if (mXwalkView != null) {
123 * mXwalkView.onNewIntent(intent);
129 @XWalkAPI(extendClass = FrameLayout.class, createExternally = true)
130 public class XWalkViewInternal extends android.widget.FrameLayout {
132 private class XWalkActivityStateListener implements ActivityStateListener {
133 WeakReference<XWalkViewInternal> mXWalkViewRef;
135 XWalkActivityStateListener(XWalkViewInternal view) {
136 mXWalkViewRef = new WeakReference<XWalkViewInternal>(view);
140 public void onActivityStateChange(Activity activity, int newState) {
141 XWalkViewInternal view = mXWalkViewRef.get();
142 if (view == null) return;
143 view.onActivityStateChange(activity, newState);
147 static final String PLAYSTORE_DETAIL_URI = "market://details?id=";
149 private XWalkContent mContent;
150 private Activity mActivity;
151 private Context mContext;
152 private boolean mIsHidden;
153 private XWalkActivityStateListener mActivityStateListener;
156 * Normal reload mode as default.
160 public static final int RELOAD_NORMAL = 0;
162 * Reload mode with bypassing the cache.
166 public static final int RELOAD_IGNORE_CACHE = 1;
169 * Constructor for inflating via XML.
170 * @param context a Context object used to access application assets.
171 * @param attrs an AttributeSet passed to our parent.
174 @XWalkAPI(preWrapperLines = {
175 " super(${param1}, ${param2});"},
177 " if (bridge == null) return;",
178 " addView((FrameLayout)bridge, new FrameLayout.LayoutParams(",
179 " FrameLayout.LayoutParams.MATCH_PARENT,",
180 " FrameLayout.LayoutParams.MATCH_PARENT));"})
181 public XWalkViewInternal(Context context, AttributeSet attrs) {
182 super(convertContext(context), attrs);
185 mActivity = (Activity) context;
186 mContext = getContext();
187 init(mContext, attrs);
191 * Constructor for Crosswalk runtime. In shared mode, context isi
192 * different from activity. In embedded mode, they're same.
193 * @param context a Context object used to access application assets
194 * @param activity the activity for this XWalkViewInternal.
197 @XWalkAPI(preWrapperLines = {
198 " super(${param1}, null);"},
200 " if (bridge == null) return;",
201 " addView((FrameLayout)bridge, new FrameLayout.LayoutParams(",
202 " FrameLayout.LayoutParams.MATCH_PARENT,",
203 " FrameLayout.LayoutParams.MATCH_PARENT));"})
204 public XWalkViewInternal(Context context, Activity activity) {
205 super(convertContext(context), null);
208 // Make sure mActivity is initialized before calling 'init' method.
209 mActivity = activity;
210 mContext = getContext();
211 init(mContext, null);
214 private static Context convertContext(Context context) {
215 Context ret = context;
216 Context bridgeContext = ReflectionHelper.getBridgeContext();
217 if (bridgeContext == null || context == null ||
218 bridgeContext.getPackageName().equals(context.getPackageName())) {
219 // Not acrossing package
222 ret = new MixedContext(bridgeContext, context);
228 * Get the current activity passed from callers. It's never null.
229 * @return the activity instance passed from callers.
233 public Activity getActivity() {
234 if (mActivity != null) {
236 } else if (getContext() instanceof Activity) {
237 return (Activity)getContext();
240 // Never achieve here.
245 // TODO(yongsheng): we should remove this since we have getContext()?
249 public Context getViewContext() {
253 public void completeWindowCreation(XWalkViewInternal newXWalkView) {
254 mContent.supplyContentsForPopup(newXWalkView == null ? null : newXWalkView.mContent);
257 private void init(Context context, AttributeSet attrs) {
258 // Initialize chromium resources. Assign them the correct ids in
260 XWalkInternalResources.resetIds(context);
262 // Intialize library, paks and others.
264 XWalkViewDelegate.init(this);
265 mActivityStateListener = new XWalkActivityStateListener(this);
266 ApplicationStatus.registerStateListenerForActivity(
267 mActivityStateListener, getActivity());
268 } catch (Throwable e) {
269 // Try to find if there is UnsatisfiedLinkError in the cause chain of the met Throwable.
270 Throwable linkError = e;
272 if (linkError == null) throw new RuntimeException(e);
273 if (linkError instanceof UnsatisfiedLinkError) break;
274 if (linkError.getCause() == null ||
275 linkError.getCause().equals(linkError)) {
276 throw new RuntimeException(e);
278 linkError = linkError.getCause();
280 final UnsatisfiedLinkError err = (UnsatisfiedLinkError) linkError;
281 final Activity activity = getActivity();
282 final String packageName = context.getPackageName();
283 String missingArch = XWalkViewDelegate.isRunningOnIA() ? "Intel" : "ARM";
284 final String message =
285 context.getString(R.string.cpu_arch_mismatch_message, missingArch);
287 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
288 builder.setTitle(R.string.cpu_arch_mismatch_title)
290 .setOnCancelListener(new DialogInterface.OnCancelListener() {
292 public void onCancel(DialogInterface dialog) {
295 }).setPositiveButton(R.string.goto_store_button_label,
296 new DialogInterface.OnClickListener() {
298 public void onClick(DialogInterface dialog, int which) {
299 activity.startActivity(new Intent(Intent.ACTION_VIEW,
300 Uri.parse(PLAYSTORE_DETAIL_URI + packageName)));
303 }).setNeutralButton(R.string.report_feedback_button_label,
304 new DialogInterface.OnClickListener() {
306 public void onClick(DialogInterface dialog, int which) {
307 ApplicationErrorReport report = new ApplicationErrorReport();
308 report.type = ApplicationErrorReport.TYPE_CRASH;
309 report.packageName = report.processName = packageName;
311 ApplicationErrorReport.CrashInfo crash =
312 new ApplicationErrorReport.CrashInfo();
313 crash.exceptionClassName = err.getClass().getSimpleName();
314 crash.exceptionMessage = "CPU architecture mismatch";
315 StringWriter writer = new StringWriter();
316 PrintWriter print = new PrintWriter(writer);
317 err.printStackTrace(print);
318 crash.stackTrace = writer.toString();
319 StackTraceElement stack = err.getStackTrace()[0];
320 crash.throwClassName = stack.getClassName();
321 crash.throwFileName = stack.getFileName();
322 crash.throwLineNumber = stack.getLineNumber();
323 crash.throwMethodName = stack.getMethodName();
325 report.crashInfo = crash;
326 report.systemApp = false;
327 report.time = System.currentTimeMillis();
329 Intent intent = new Intent(Intent.ACTION_APP_ERROR);
330 intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
331 activity.startActivity(intent);
335 builder.create().show();
339 initXWalkContent(context, attrs);
342 private void initXWalkContent(Context context, AttributeSet attrs) {
344 mContent = new XWalkContent(context, attrs, this);
346 new FrameLayout.LayoutParams(
347 FrameLayout.LayoutParams.MATCH_PARENT,
348 FrameLayout.LayoutParams.MATCH_PARENT));
351 // Set default XWalkClientImpl.
352 setXWalkClient(new XWalkClient(this));
353 // Set default XWalkWebChromeClient and DownloadListener. The default actions
354 // are provided via the following clients if special actions are not needed.
355 setXWalkWebChromeClient(new XWalkWebChromeClient(this));
357 // Set with internal implementation. Could be overwritten by embedders'
359 setUIClient(new XWalkUIClientInternal(this));
360 setResourceClient(new XWalkResourceClientInternal(this));
362 setDownloadListener(new XWalkDownloadListenerImpl(context));
363 setNavigationHandler(new XWalkNavigationHandlerImpl(context));
364 setNotificationService(new XWalkNotificationServiceImpl(context, this));
366 if (!CommandLine.getInstance().hasSwitch("disable-xwalk-extensions")) {
367 BuiltinXWalkExtensions.load(context, getActivity());
369 XWalkPreferencesInternal.setValue(XWalkPreferencesInternal.ENABLE_EXTENSIONS, false);
372 XWalkPathHelper.initialize();
373 XWalkPathHelper.setCacheDirectory(
374 mContext.getApplicationContext().getCacheDir().getPath());
376 String state = Environment.getExternalStorageState();
377 if (Environment.MEDIA_MOUNTED.equals(state) ||
378 Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
379 File extCacheDir = mContext.getApplicationContext().getExternalCacheDir();
380 if (null != extCacheDir) {
381 XWalkPathHelper.setExternalCacheDirectory(extCacheDir.getPath());
387 * Load a web page/app from a given base URL or a content.
388 * If url is null or empty and content is null or empty, then this function
390 * If content is not null, load the web page/app from the content.
391 * If content is not null and the url is not set, return "about:blank" ifi
392 * calling {@link XWalkViewInternal#getUrl()}.
393 * If content is null, try to load the content from the url.
395 * It supports URL schemes like 'http:', 'https:' and 'file:'.
396 * It can also load files from Android assets, e.g. 'file:///android_asset/'.
397 * @param url the url for web page/app.
398 * @param content the content for the web page/app. Could be empty.
402 public void load(String url, String content) {
403 if (mContent == null) return;
405 mContent.loadUrl(url, content);
409 * Load a web app from a given manifest.json file. If content is not null,
410 * load the manifest.json from the content. If content is null, try to load
411 * the manifest.json from the url. Note that url should not be null if the
412 * launched path defined in manifest.json is relative.
414 * It supports URL schemes like 'http:', 'https:' and 'file:'.
415 * It can also load files from Android assets, e.g. 'file:///android_asset/'.
416 * @param url the url for manifest.json.
417 * @param content the content for manifest.json.
421 public void loadAppFromManifest(String url, String content) {
422 if (mContent == null) return;
424 mContent.loadAppFromManifest(url, content);
428 * Reload a web app with a given mode.
429 * @param mode the reload mode.
433 public void reload(int mode) {
434 if (mContent == null) return;
436 mContent.reload(mode);
440 * Stop current loading progress.
444 public void stopLoading() {
445 if (mContent == null) return;
447 mContent.stopLoading();
451 * Get the url of current web page/app. This may be different from what's passed
453 * @return the url for current web page/app.
457 public String getUrl() {
458 if (mContent == null) return null;
460 return mContent.getUrl();
464 * Get the title of current web page/app. This may be different from what's passed
466 * @return the title for current web page/app.
470 public String getTitle() {
471 if (mContent == null) return null;
473 return mContent.getTitle();
477 * Get the original url specified by caller.
478 * @return the original url.
482 public String getOriginalUrl() {
483 if (mContent == null) return null;
485 return mContent.getOriginalUrl();
489 * Get the navigation history for current XWalkViewInternal. It's synchronized with
490 * this XWalkViewInternal if any backward/forward and navigation operations.
491 * @return the navigation history.
495 public XWalkNavigationHistoryInternal getNavigationHistory() {
496 if (mContent == null) return null;
498 return mContent.getNavigationHistory();
502 * Injects the supplied Java object into this XWalkViewInternal.
503 * Each method defined in the class of the object should be
504 * marked with {@link JavascriptInterface} if it's called by JavaScript.
505 * @param object the supplied Java object, called by JavaScript.
506 * @param name the name injected in JavaScript.
510 public void addJavascriptInterface(Object object, String name) {
511 if (mContent == null) return;
513 mContent.addJavascriptInterface(object, name);
517 * Evaluate a fragment of JavaScript code and get the result via callback.
518 * @param script the JavaScript string.
519 * @param callback the callback to handle the evaluated result.
523 public void evaluateJavascript(String script, ValueCallback<String> callback) {
524 if (mContent == null) return;
526 mContent.evaluateJavascript(script, callback);
530 * Clear the resource cache. Note that the cache is per-application, so this
531 * will clear the cache for all XWalkViews used.
532 * @param includeDiskFiles indicate whether to clear disk files for cache.
536 public void clearCache(boolean includeDiskFiles) {
537 if (mContent == null) return;
539 mContent.clearCache(includeDiskFiles);
543 * Indicate that a HTML element is occupying the whole screen.
544 * @return true if any HTML element is occupying the whole screen.
548 public boolean hasEnteredFullscreen() {
549 if (mContent == null) return false;
551 return mContent.hasEnteredFullscreen();
555 * Leave fullscreen mode if it's. Do nothing if it's not
560 public void leaveFullscreen() {
561 if (mContent == null) return;
563 mContent.exitFullscreen();
567 * Pause all layout, parsing and JavaScript timers for all XWalkViewInternal instances.
568 * It will be called when the container Activity get paused. It can also be explicitly
569 * called to pause timers.
571 * Note that it will globally impact all XWalkViewInternal instances, not limited to
572 * just this XWalkViewInternal.
577 public void pauseTimers() {
578 if (mContent == null) return;
580 mContent.pauseTimers();
584 * Resume all layout, parsing and JavaScript timers for all XWalkViewInternal instances.
585 * It will be called when the container Activity get resumed. It can also be explicitly
586 * called to resume timers.
588 * Note that it will globally impact all XWalkViewInternal instances, not limited to
589 * just this XWalkViewInternal.
594 public void resumeTimers() {
595 if (mContent == null) return;
597 mContent.resumeTimers();
601 * Pause many other things except JavaScript timers inside rendering engine,
602 * like video player, modal dialogs, etc. See {@link #pauseTimers} about pausing
604 * It will be called when the container Activity get paused. It can also be explicitly
605 * called to pause above things.
609 public void onHide() {
610 if (mContent == null || mIsHidden) return;
616 * Resume video player, modal dialogs. Embedders are in charge of calling
617 * this during resuming this activity if they call onHide.
618 * Typically it should be called when the activity for this view is resumed.
619 * It will be called when the container Activity get resumed. It can also be explicitly
620 * called to resume above things.
624 public void onShow() {
625 if (mContent == null || !mIsHidden ) return;
631 * Release internal resources occupied by this XWalkViewInternal.
632 * It will be called when the container Activity get destroyed. It can also be explicitly
633 * called to release resources.
637 public void onDestroy() {
642 * Pass through activity result to XWalkViewInternal. Many internal facilities need this
643 * to handle activity result like JavaScript dialog, Crosswalk extensions, etc.
644 * See <a href="http://developer.android.com/reference/android/app/Activity.html">
645 * android.app.Activity.onActivityResult()</a>.
646 * @param requestCode passed from android.app.Activity.onActivityResult().
647 * @param resultCode passed from android.app.Activity.onActivityResult().
648 * @param data passed from android.app.Activity.onActivityResult().
652 public void onActivityResult(int requestCode, int resultCode, Intent data) {
653 if (mContent == null) return;
654 mContent.onActivityResult(requestCode, resultCode, data);
658 * Pass through intents to XWalkViewInternal. Many internal facilities need this
659 * to receive the intents like web notification. See
660 * <a href="http://developer.android.com/reference/android/app/Activity.html">
661 * android.app.Activity.onNewIntent()</a>.
662 * @param intent passed from android.app.Activity.onNewIntent().
666 public boolean onNewIntent(Intent intent) {
667 if (mContent == null) return false;
668 return mContent.onNewIntent(intent);
672 * Save current internal state of this XWalkViewInternal. This can help restore this state
673 * afterwards restoring.
674 * @param outState the saved state for restoring.
678 public boolean saveState(Bundle outState) {
679 if (mContent == null) return false;
680 mContent.saveState(outState);
685 * Restore the state from the saved bundle data.
686 * @param inState the state saved from saveState().
687 * @return true if it can restore the state.
691 public boolean restoreState(Bundle inState) {
692 if (mContent == null) return false;
693 if (mContent.restoreState(inState) != null) return true;
698 * Get the API version of Crosswalk embedding API.
699 * @return the string of API level.
702 // TODO(yongsheng): make it static?
704 public String getAPIVersion() {
709 * Get the Crosswalk version.
710 * @return the string of Crosswalk.
713 // TODO(yongsheng): make it static?
715 public String getXWalkVersion() {
716 if (mContent == null) return null;
717 return mContent.getXWalkVersion();
721 * Embedders use this to customize their handlers to events/callbacks related
723 * @param client the XWalkUIClientInternal defined by callers.
727 public void setUIClient(XWalkUIClientInternal client) {
728 if (mContent == null) return;
730 mContent.setUIClient(client);
734 * Embedders use this to customize their handlers to events/callbacks related
735 * to resource loading.
736 * @param client the XWalkResourceClientInternal defined by callers.
740 public void setResourceClient(XWalkResourceClientInternal client) {
741 if (mContent == null) return;
743 mContent.setResourceClient(client);
746 // TODO(yongsheng): this is not public.
750 public XWalkSettings getSettings() {
751 if (mContent == null) return null;
753 return mContent.getSettings();
757 * This method is used by Cordova for hacking.
758 * TODO(yongsheng): remove this and related test cases?
763 public void setNetworkAvailable(boolean networkUp) {
764 if (mContent == null) return;
766 mContent.setNetworkAvailable(networkUp);
770 * Enables remote debugging and returns the URL at which the dev tools
771 * server is listening for commands.
772 * The allowedUid argument can be used to specify the uid of the process
773 * that is permitted to connect.
774 * TODO(wang16): Hide or remove this API after new API for getting remote
775 * debugging url available.
780 public String enableRemoteDebugging(int allowedUid) {
781 if (mContent == null) return null;
783 return mContent.enableRemoteDebugging(allowedUid);
787 * It's used for Presentation API.
790 public int getContentID() {
791 if (mContent == null) return -1;
792 return mContent.getRoutingID();
795 boolean canGoBack() {
796 if (mContent == null) return false;
798 return mContent.canGoBack();
802 if (mContent == null) return;
807 boolean canGoForward() {
808 if (mContent == null) return false;
810 return mContent.canGoForward();
814 if (mContent == null) return;
816 mContent.goForward();
819 void clearHistory() {
820 if (mContent == null) return;
822 mContent.clearHistory();
826 if (mContent == null) return;
827 ApplicationStatus.unregisterActivityStateListener(mActivityStateListener);
828 mActivityStateListener = null;
830 disableRemoteDebugging();
833 // Enables remote debugging and returns the URL at which the dev tools server is listening
834 // for commands. Only the current process is allowed to connect to the server.
835 String enableRemoteDebugging() {
836 return enableRemoteDebugging(mContext.getApplicationInfo().uid);
839 void disableRemoteDebugging() {
840 if (mContent == null) return;
842 mContent.disableRemoteDebugging();
845 private static void checkThreadSafety() {
846 if (Looper.myLooper() != Looper.getMainLooper()) {
847 Throwable throwable = new Throwable(
848 "Warning: A XWalkViewInternal method was called on thread '" +
849 Thread.currentThread().getName() + "'. " +
850 "All XWalkViewInternal methods must be called on the UI thread. ");
851 throw new RuntimeException(throwable);
855 boolean isOwnerActivityRunning() {
856 int status = ApplicationStatus.getStateForActivity(getActivity());
857 if (status == ActivityState.DESTROYED) return false;
861 void navigateTo(int offset) {
862 if (mContent == null) return;
863 mContent.navigateTo(offset);
866 void setOverlayVideoMode(boolean enabled) {
867 mContent.setOverlayVideoMode(enabled);
870 // Below methods are for test shell and instrumentation tests.
874 public void setXWalkClient(XWalkClient client) {
875 if (mContent == null) return;
877 mContent.setXWalkClient(client);
883 public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
884 if (mContent == null) return;
886 mContent.setXWalkWebChromeClient(client);
892 public void setDownloadListener(DownloadListener listener) {
893 if (mContent == null) return;
895 mContent.setDownloadListener(listener);
901 public void setNavigationHandler(XWalkNavigationHandler handler) {
902 if (mContent == null) return;
904 mContent.setNavigationHandler(handler);
910 public void setNotificationService(XWalkNotificationService service) {
911 if (mContent == null) return;
913 mContent.setNotificationService(service);
920 public boolean dispatchKeyEvent(KeyEvent event) {
921 if (event.getAction() == KeyEvent.ACTION_UP &&
922 event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
923 // If there's navigation happens when app is fullscreen,
924 // the content will still be fullscreen after navigation.
925 // In such case, the back key will exit fullscreen first.
926 if (hasEnteredFullscreen()) {
929 } else if (canGoBack()) {
934 return super.dispatchKeyEvent(event);
937 private void onActivityStateChange(Activity activity, int newState) {
938 assert(getActivity() == activity);
940 case ActivityState.STARTED:
943 case ActivityState.PAUSED:
946 case ActivityState.RESUMED:
949 case ActivityState.DESTROYED:
952 case ActivityState.STOPPED: