From a652d892ca93d077038310af50d2a40ab5fabfd6 Mon Sep 17 00:00:00 2001
From: Tony Homer <tony.homer@intel.com>
Date: Wed, 8 Apr 2015 15:00:38 -0400
Subject: [PATCH] CB-8684 Add onStart/onStop hooks for plugins (close #173)

---
 .../org/apache/cordova/CordovaActivity.java   | 28 ++++++++
 .../src/org/apache/cordova/CordovaPlugin.java | 12 ++++
 .../org/apache/cordova/CordovaWebView.java    |  4 ++
 .../apache/cordova/CordovaWebViewImpl.java    | 15 ++++
 .../src/org/apache/cordova/PluginManager.java | 22 ++++++
 .../cordova/test/CordovaPluginTest.java       | 70 +++++++++++++++++++
 test/res/xml/config.xml                       |  4 ++
 .../apache/cordova/test/LifeCyclePlugin.java  | 49 +++++++++++++
 8 files changed, 204 insertions(+)
 create mode 100644 test/androidTest/src/org/apache/cordova/test/CordovaPluginTest.java
 create mode 100644 test/src/org/apache/cordova/test/LifeCyclePlugin.java

diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java
index 8e185dde..e1f917cd 100755
--- a/framework/src/org/apache/cordova/CordovaActivity.java
+++ b/framework/src/org/apache/cordova/CordovaActivity.java
@@ -268,6 +268,34 @@ public class CordovaActivity extends Activity {
         this.appView.handleResume(this.keepRunning);
     }
 
+    /**
+     * Called when the activity is no longer visible to the user.
+     */
+    @Override
+    protected void onStop() {
+        super.onStop();
+        LOG.d(TAG, "Stopped the activity.");
+
+        if (this.appView == null) {
+            return;
+        }
+        this.appView.handleStop();
+    }
+
+    /**
+     * Called when the activity is becoming visible to the user.
+     */
+    @Override
+    protected void onStart() {
+        super.onStart();
+        LOG.d(TAG, "Started the activity.");
+
+        if (this.appView == null) {
+            return;
+        }
+        this.appView.handleStart();
+    }
+
     /**
      * The final call you receive before your activity is destroyed.
      */
diff --git a/framework/src/org/apache/cordova/CordovaPlugin.java b/framework/src/org/apache/cordova/CordovaPlugin.java
index 0d081178..7cf8528f 100644
--- a/framework/src/org/apache/cordova/CordovaPlugin.java
+++ b/framework/src/org/apache/cordova/CordovaPlugin.java
@@ -148,6 +148,18 @@ public class CordovaPlugin {
     public void onResume(boolean multitasking) {
     }
 
+    /**
+     * Called when the activity is becoming visible to the user.
+     */
+    public void onStart() {
+    }
+
+    /**
+     * Called when the activity is no longer visible to the user.
+     */
+    public void onStop() {
+    }
+
     /**
      * Called when the activity receives a new intent.
      */
diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java
index d3071f41..52dfb92c 100644
--- a/framework/src/org/apache/cordova/CordovaWebView.java
+++ b/framework/src/org/apache/cordova/CordovaWebView.java
@@ -61,6 +61,10 @@ public interface CordovaWebView {
 
     void handleResume(boolean keepRunning);
 
+    void handleStart();
+
+    void handleStop();
+
     void handleDestroy();
 
     /**
diff --git a/framework/src/org/apache/cordova/CordovaWebViewImpl.java b/framework/src/org/apache/cordova/CordovaWebViewImpl.java
index 7115db41..868ad4f9 100644
--- a/framework/src/org/apache/cordova/CordovaWebViewImpl.java
+++ b/framework/src/org/apache/cordova/CordovaWebViewImpl.java
@@ -115,6 +115,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
 
         pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid");
         pluginManager.init();
+
     }
 
     @Override
@@ -446,6 +447,20 @@ public class CordovaWebViewImpl implements CordovaWebView {
         this.pluginManager.onResume(keepRunning);
         sendJavascriptEvent("resume");
     }
+    @Override
+    public void handleStart() {
+        if (!isInitialized()) {
+            return;
+        }
+        pluginManager.onStart();
+    }
+    @Override
+    public void handleStop() {
+        if (!isInitialized()) {
+            return;
+        }
+        pluginManager.onStop();
+    }
 
     @Override
     public void handleDestroy() {
diff --git a/framework/src/org/apache/cordova/PluginManager.java b/framework/src/org/apache/cordova/PluginManager.java
index 87663be9..a541e770 100755
--- a/framework/src/org/apache/cordova/PluginManager.java
+++ b/framework/src/org/apache/cordova/PluginManager.java
@@ -264,6 +264,28 @@ public class PluginManager {
         }
     }
 
+    /**
+     * Called when the activity is becoming visible to the user.
+     */
+    public void onStart() {
+        for (CordovaPlugin plugin : this.pluginMap.values()) {
+            if (plugin != null) {
+                plugin.onStart();
+            }
+        }
+    }
+
+    /**
+     * Called when the activity is no longer visible to the user.
+     */
+    public void onStop() {
+        for (CordovaPlugin plugin : this.pluginMap.values()) {
+            if (plugin != null) {
+                plugin.onStop();
+            }
+        }
+    }
+
     /**
      * The final call you receive before your activity is destroyed.
      */
diff --git a/test/androidTest/src/org/apache/cordova/test/CordovaPluginTest.java b/test/androidTest/src/org/apache/cordova/test/CordovaPluginTest.java
new file mode 100644
index 00000000..f9f254aa
--- /dev/null
+++ b/test/androidTest/src/org/apache/cordova/test/CordovaPluginTest.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.cordova.test;
+
+
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+
+import org.apache.cordova.CordovaWebView;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+public class CordovaPluginTest extends BaseCordovaIntegrationTest {
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        setUpWithStartUrl(null);
+    }
+
+    private void invokeBlockingCallToLifeCycleEvent(final String lifeCycleEventName) {
+        testActivity.runOnUiThread( new Runnable() {
+            public void run() {
+                try {
+                    Method method = getInstrumentation().getClass().getMethod(lifeCycleEventName, Activity.class);
+                    method.invoke(getInstrumentation(), testActivity);
+                } catch (Exception e) {
+                    fail("An Exception occurred in invokeBlockingCallToLifeCycleEvent while invoking " + lifeCycleEventName);
+                }
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+    }
+
+    public void testPluginLifeCycle() throws IOException {
+        //TODO: add coverage for both cases where handleOnStart is called in CordovaActivity (onStart and init)
+        //currently only one of the cases is covered
+        //TODO: add coverage for both cases where onStart is called in CordovaWebViewImpl (handleOnStart and init)
+        //currently only one of the cases is covered
+        LifeCyclePlugin testPlugin = (LifeCyclePlugin)cordovaWebView.getPluginManager().getPlugin("LifeCycle");
+
+        assertEquals("start,pause,stop,", testPlugin.calls);
+        testPlugin.calls = "";
+        // testOnStart
+        invokeBlockingCallToLifeCycleEvent("callActivityOnStart");
+        invokeBlockingCallToLifeCycleEvent("callActivityOnResume");
+        invokeBlockingCallToLifeCycleEvent("callActivityOnPause");
+        invokeBlockingCallToLifeCycleEvent("callActivityOnStop");
+        assertEquals("start,resume,pause,stop,", testPlugin.calls);
+    }
+}
diff --git a/test/res/xml/config.xml b/test/res/xml/config.xml
index 894c3362..7da46385 100644
--- a/test/res/xml/config.xml
+++ b/test/res/xml/config.xml
@@ -38,4 +38,8 @@
     <feature name="Activity">
         <param name="android-package" value="org.apache.cordova.test.ActivityPlugin" />
     </feature>
+    <feature name="LifeCycle">
+        <param name="android-package" value="org.apache.cordova.test.LifeCyclePlugin" />
+        <param name="onload" value="true" />
+    </feature>
 </widget>
diff --git a/test/src/org/apache/cordova/test/LifeCyclePlugin.java b/test/src/org/apache/cordova/test/LifeCyclePlugin.java
new file mode 100644
index 00000000..62ab7faa
--- /dev/null
+++ b/test/src/org/apache/cordova/test/LifeCyclePlugin.java
@@ -0,0 +1,49 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+package org.apache.cordova.test;
+
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.LOG;
+
+public class LifeCyclePlugin extends CordovaPlugin {
+
+    static String TAG = "LifeCyclePlugin";
+    String calls = "";
+
+    @Override
+    public void onStart() {
+        calls += "start,";
+        LOG.d(TAG, "onStart");
+    }
+    @Override
+    public void onPause(boolean multitasking) {
+        calls += "pause,";
+        LOG.d(TAG, "onPause");
+    }
+    @Override
+    public void onResume(boolean multitasking) {
+        calls += "resume,";
+        LOG.d(TAG, "onResume");
+    }
+    @Override
+    public void onStop() {
+        calls += "stop,";
+        LOG.d(TAG, "onStop");
+    }
+}