Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / XWalkViewInternal.java
index 878f897..2a44fb5 100644 (file)
@@ -24,9 +24,12 @@ import android.widget.FrameLayout;
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.ref.WeakReference;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ApplicationStatus.ActivityStateListener;
+import org.chromium.base.CommandLine;
 
 import org.xwalk.core.internal.extension.XWalkExtensionManager;
 import org.xwalk.core.internal.extension.XWalkPathHelper;
@@ -55,13 +58,15 @@ import org.xwalk.core.internal.extension.XWalkPathHelper;
  * {@link XWalkUIClientInternal} for listening to the events related to resource loading and UI.
  * By default, Crosswalk has a default implementation. Callers can override them if needed.</p>
  *
- * <p>Unlike other Android views, this class has to listen to system events like application life
- * cycle, intents, and activity result. The web engine inside this view need to get and handle
- * them. And the onDestroy() method of XWalkViewInternal MUST be called explicitly when an XWalkViewInternal
- * won't be used anymore, otherwise it will cause the memory leak from the native side of the web
- * engine. It's similar to the 
- * <a href="http://developer.android.com/reference/android/webkit/WebView.html#destroy()">
- * destroy()</a> method of Android WebView. For example:</p>
+ * <p>Unlike other Android views, this class has to listen to system events like intents and activity result.
+ * The web engine inside this view need to get and handle them.
+ * With contianer activity's lifecycle change, XWalkViewInternal will pause all timers and other
+ * components like videos when activity paused, resume back them when activity resumed.
+ * When activity is about to destroy, XWalkViewInternal will destroy itself as well.
+ * Embedders can also call onHide() and pauseTimers() to explicitly pause XWalkViewInternal.
+ * Similarily with onShow(), resumeTimers() and onDestroy().
+ *
+ * For example:</p>
  *
  * <pre>
  *   import android.app.Activity;
@@ -108,32 +113,6 @@ import org.xwalk.core.internal.extension.XWalkPathHelper;
  *       }
  *
  *       &#64;Override
- *       protected void onPause() {
- *           super.onPause();
- *           if (mXwalkView != null) {
- *               mXwalkView.pauseTimers();
- *               mXwalkView.onHide();
- *           }
- *       }
- *
- *       &#64;Override
- *       protected void onResume() {
- *           super.onResume();
- *           if (mXwalkView != null) {
- *               mXwalkView.resumeTimers();
- *               mXwalkView.onShow();
- *           }
- *       }
- *
- *       &#64;Override
- *       protected void onDestroy() {
- *           super.onDestroy();
- *           if (mXwalkView != null) {
- *               mXwalkView.onDestroy();
- *           }
- *       }
- *
- *       &#64;Override
  *       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  *           if (mXwalkView != null) {
  *               mXwalkView.onActivityResult(requestCode, resultCode, data);
@@ -149,8 +128,24 @@ import org.xwalk.core.internal.extension.XWalkPathHelper;
  *   }
  * </pre>
  */
+@XWalkAPI(extendClass = FrameLayout.class, createExternally = true)
 public class XWalkViewInternal extends android.widget.FrameLayout {
 
+    private class XWalkActivityStateListener implements ActivityStateListener {
+        WeakReference<XWalkViewInternal> mXWalkViewRef;
+
+        XWalkActivityStateListener(XWalkViewInternal view) {
+            mXWalkViewRef = new WeakReference<XWalkViewInternal>(view);
+        }
+
+        @Override
+        public void onActivityStateChange(Activity activity, int newState) {
+            XWalkViewInternal view = mXWalkViewRef.get();
+            if (view == null) return;
+            view.onActivityStateChange(activity, newState);
+        }
+    }
+
     static final String PLAYSTORE_DETAIL_URI = "market://details?id=";
 
     private XWalkContent mContent;
@@ -158,16 +153,19 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
     private Context mContext;
     private XWalkExtensionManager mExtensionManager;
     private boolean mIsHidden;
+    private XWalkActivityStateListener mActivityStateListener;
 
     /**
      * Normal reload mode as default.
      * @since 1.0
      */
+    @XWalkAPI
     public static final int RELOAD_NORMAL = 0;
     /**
      * Reload mode with bypassing the cache.
      * @since 1.0
      */
+    @XWalkAPI
     public static final int RELOAD_IGNORE_CACHE = 1;
 
     /**
@@ -176,6 +174,12 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param attrs    an AttributeSet passed to our parent.
      * @since 1.0
      */
+    @XWalkAPI(preWrapperLines = {
+                  "        super(${param1}, ${param2});"},
+              postWrapperLines = {
+                  "        addView((FrameLayout)bridge, new FrameLayout.LayoutParams(",
+                  "                FrameLayout.LayoutParams.MATCH_PARENT,",
+                  "                FrameLayout.LayoutParams.MATCH_PARENT));"})
     public XWalkViewInternal(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -191,6 +195,12 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param activity the activity for this XWalkViewInternal.
      * @since 1.0
      */
+    @XWalkAPI(preWrapperLines = {
+                  "        super(${param1}, null);"},
+              postWrapperLines = {
+                  "        addView((FrameLayout)bridge, new FrameLayout.LayoutParams(",
+                  "                FrameLayout.LayoutParams.MATCH_PARENT,",
+                  "                FrameLayout.LayoutParams.MATCH_PARENT));"})
     public XWalkViewInternal(Context context, Activity activity) {
         super(context, null);
         checkThreadSafety();
@@ -235,6 +245,9 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
         // Intialize library, paks and others.
         try {
             XWalkViewDelegate.init(this);
+            mActivityStateListener = new XWalkActivityStateListener(this);
+            ApplicationStatus.registerStateListenerForActivity(
+                    mActivityStateListener, getActivity());
         } catch (Throwable e) {
             // Try to find if there is UnsatisfiedLinkError in the cause chain of the met Throwable.
             Throwable linkError = e;
@@ -333,10 +346,12 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
         setNavigationHandler(new XWalkNavigationHandlerImpl(context));
         setNotificationService(new XWalkNotificationServiceImpl(context, this));
 
-        // Enable xwalk extension mechanism and start load extensions here.
-        // Note that it has to be after above initialization.
-        mExtensionManager = new XWalkExtensionManager(context, getActivity());
-        mExtensionManager.loadExtensions();
+        if (!CommandLine.getInstance().hasSwitch("disable-xwalk-extensions")) {
+            // Enable xwalk extension mechanism and start load extensions here.
+            // Note that it has to be after above initialization.
+            mExtensionManager = new XWalkExtensionManager(context, getActivity());
+            mExtensionManager.loadExtensions();
+        }
 
         XWalkPathHelper.initialize();
         XWalkPathHelper.setCacheDirectory(
@@ -367,6 +382,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param content the content for the web page/app. Could be empty.
      * @since 1.0
      */
+    @XWalkAPI
     public void load(String url, String content) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -385,6 +401,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param content the content for manifest.json.
      * @since 1.0
      */
+    @XWalkAPI
     public void loadAppFromManifest(String url, String content) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -396,6 +413,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param mode the reload mode.
      * @since 1.0
      */
+    @XWalkAPI
     public void reload(int mode) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -406,6 +424,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * Stop current loading progress.
      * @since 1.0
      */
+    @XWalkAPI
     public void stopLoading() {
         if (mContent == null) return;
         checkThreadSafety();
@@ -418,6 +437,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @return the url for current web page/app.
      * @since 1.0
      */
+    @XWalkAPI
     public String getUrl() {
         if (mContent == null) return null;
         checkThreadSafety();
@@ -430,6 +450,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @return the title for current web page/app.
      * @since 1.0
      */
+    @XWalkAPI
     public String getTitle() {
         if (mContent == null) return null;
         checkThreadSafety();
@@ -441,6 +462,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @return the original url.
      * @since 1.0
      */
+    @XWalkAPI
     public String getOriginalUrl() {
         if (mContent == null) return null;
         checkThreadSafety();
@@ -453,6 +475,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @return the navigation history.
      * @since 1.0
      */
+    @XWalkAPI
     public XWalkNavigationHistoryInternal getNavigationHistory() {
         if (mContent == null) return null;
         checkThreadSafety();
@@ -467,6 +490,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param name the name injected in JavaScript.
      * @since 1.0
      */
+    @XWalkAPI
     public void addJavascriptInterface(Object object, String name) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -479,6 +503,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param callback the callback to handle the evaluated result.
      * @since 1.0
      */
+    @XWalkAPI
     public void evaluateJavascript(String script, ValueCallback<String> callback) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -491,6 +516,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param includeDiskFiles indicate whether to clear disk files for cache.
      * @since 1.0
      */
+    @XWalkAPI
     public void clearCache(boolean includeDiskFiles) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -502,6 +528,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @return true if any HTML element is occupying the whole screen.
      * @since 1.0
      */
+    @XWalkAPI
     public boolean hasEnteredFullscreen() {
         if (mContent == null) return false;
         checkThreadSafety();
@@ -513,6 +540,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * in fullscreen.
      * @since 1.0
      */
+    @XWalkAPI
     public void leaveFullscreen() {
         if (mContent == null) return;
         checkThreadSafety();
@@ -521,15 +549,15 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
 
     /**
      * Pause all layout, parsing and JavaScript timers for all XWalkViewInternal instances.
-     * Typically it should be called when the activity for this view is paused,
-     * and accordingly {@link #resumeTimers} should be called when the activity
-     * is resumed again.
+     * It will be called when the container Activity get paused. It can also be explicitly
+     * called to pause timers.
      *
      * Note that it will globally impact all XWalkViewInternal instances, not limited to
      * just this XWalkViewInternal.
      *
      * @since 1.0
      */
+    @XWalkAPI
     public void pauseTimers() {
         if (mContent == null) return;
         checkThreadSafety();
@@ -538,13 +566,15 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
 
     /**
      * Resume all layout, parsing and JavaScript timers for all XWalkViewInternal instances.
-     * Typically it should be called when the activity for this view is resumed.
+     * It will be called when the container Activity get resumed. It can also be explicitly
+     * called to resume timers.
      *
      * Note that it will globally impact all XWalkViewInternal instances, not limited to
      * just this XWalkViewInternal.
      *
      * @since 1.0
      */
+    @XWalkAPI
     public void resumeTimers() {
         if (mContent == null) return;
         checkThreadSafety();
@@ -555,12 +585,14 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * Pause many other things except JavaScript timers inside rendering engine,
      * like video player, modal dialogs, etc. See {@link #pauseTimers} about pausing
      * JavaScript timers.
-     * Typically it should be called when the activity for this view is paused.
+     * It will be called when the container Activity get paused. It can also be explicitly
+     * called to pause above things.
      * @since 1.0
      */
+    @XWalkAPI
     public void onHide() {
         if (mContent == null || mIsHidden) return;
-        mExtensionManager.onPause();
+        if (null != mExtensionManager) mExtensionManager.onPause();
         mContent.onPause();
         mIsHidden = true;
     }
@@ -569,19 +601,25 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * Resume video player, modal dialogs. Embedders are in charge of calling
      * this during resuming this activity if they call onHide.
      * Typically it should be called when the activity for this view is resumed.
+     * It will be called when the container Activity get resumed. It can also be explicitly
+     * called to resume above things.
      * @since 1.0
      */
+    @XWalkAPI
     public void onShow() {
         if (mContent == null || !mIsHidden ) return;
-        mExtensionManager.onResume();
+        if (null != mExtensionManager) mExtensionManager.onResume();
         mContent.onResume();
         mIsHidden = false;
     }
 
     /**
      * Release internal resources occupied by this XWalkViewInternal.
+     * It will be called when the container Activity get destroyed. It can also be explicitly
+     * called to release resources.
      * @since 1.0
      */
+    @XWalkAPI
     public void onDestroy() {
         destroy();
     }
@@ -596,9 +634,11 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param data passed from android.app.Activity.onActivityResult().
      * @since 1.0
      */
+    @XWalkAPI
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (mContent == null) return;
-        mExtensionManager.onActivityResult(requestCode, resultCode, data);
+        if (null != mExtensionManager)
+                mExtensionManager.onActivityResult(requestCode, resultCode, data);
         mContent.onActivityResult(requestCode, resultCode, data);
     }
 
@@ -610,6 +650,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param intent passed from android.app.Activity.onNewIntent().
      * @since 1.0
      */
+    @XWalkAPI
     public boolean onNewIntent(Intent intent) {
         if (mContent == null) return false;
         return mContent.onNewIntent(intent);
@@ -621,6 +662,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param outState the saved state for restoring.
      * @since 1.0
      */
+    @XWalkAPI
     public boolean saveState(Bundle outState) {
         if (mContent == null) return false;
         mContent.saveState(outState);
@@ -633,6 +675,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @return true if it can restore the state.
      * @since 1.0
      */
+    @XWalkAPI
     public boolean restoreState(Bundle inState) {
         if (mContent == null) return false;
         if (mContent.restoreState(inState) != null) return true;
@@ -645,8 +688,9 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @since 1.0
      */
     // TODO(yongsheng): make it static?
+    @XWalkAPI
     public String getAPIVersion() {
-        return "2.0";
+        return "2.1";
     }
 
     /**
@@ -655,6 +699,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @since 1.0
      */
     // TODO(yongsheng): make it static?
+    @XWalkAPI
     public String getXWalkVersion() {
         if (mContent == null) return null;
         return mContent.getXWalkVersion();
@@ -666,6 +711,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param client the XWalkUIClientInternal defined by callers.
      * @since 1.0
      */
+    @XWalkAPI
     public void setUIClient(XWalkUIClientInternal client) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -678,6 +724,7 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
      * @param client the XWalkResourceClientInternal defined by callers.
      * @since 1.0
      */
+    @XWalkAPI
     public void setResourceClient(XWalkResourceClientInternal client) {
         if (mContent == null) return;
         checkThreadSafety();
@@ -761,7 +808,9 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
 
     void destroy() {
         if (mContent == null) return;
-        mExtensionManager.onDestroy();
+        ApplicationStatus.unregisterActivityStateListener(mActivityStateListener);
+        mActivityStateListener = null;
+        if (null != mExtensionManager) mExtensionManager.onDestroy();
         mContent.destroy();
         disableRemoteDebugging();
     }
@@ -869,4 +918,27 @@ public class XWalkViewInternal extends android.widget.FrameLayout {
         }
         return super.dispatchKeyEvent(event);
     }
+
+    private void onActivityStateChange(Activity activity, int newState) {
+        assert(getActivity() == activity);
+        switch (newState) {
+            case ActivityState.STARTED:
+                onShow();
+                break;
+            case ActivityState.PAUSED:
+                pauseTimers();
+                break;
+            case ActivityState.RESUMED:
+                resumeTimers();
+                break;
+            case ActivityState.DESTROYED:
+                onDestroy();
+                break;
+            case ActivityState.STOPPED:
+                onHide();
+                break;
+            default:
+                break;
+        }
+    }
 }