Bug 1314649 - Hacky Swrve Integration - FOR TESTING PURPOSES ONLY draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Wed, 02 Nov 2016 19:52:48 +0100
changeset 441898 0aab1da359cb9b3480f751f9a140ed53741a9e48
parent 441645 b7f895c1dc2e91530240efbf50ac063a0f8a9cb5
child 537664 f0ce110fe5a71dac90945db875deae9e76cb700d
push id36548
push users.kaspari@gmail.com
push dateMon, 21 Nov 2016 11:17:41 +0000
bugs1314649
milestone53.0a1
Bug 1314649 - Hacky Swrve Integration - FOR TESTING PURPOSES ONLY MozReview-Commit-ID: JcyGTolvzCi
build.gradle
mobile/android/app/build.gradle
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/GcmAndroidManifest_services.xml.in
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStream.java
mobile/android/base/java/org/mozilla/gecko/delegates/BrowserAppDelegate.java
mobile/android/base/java/org/mozilla/gecko/push/PushService.java
mobile/android/base/java/org/mozilla/gecko/swrve/SwrveBrowserAppDelegate.java
--- a/build.gradle
+++ b/build.gradle
@@ -9,16 +9,19 @@ allprojects {
     }
 
     repositories {
         if (gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORY) {
             maven {
                 url gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORY
             }
         }
+        jcenter {
+            url = 'http://dl.bintray.com/swrve-inc/android'
+        }
     }
 }
 
 buildDir "${topobjdir}/gradle/build"
 
 buildscript {
     repositories {
         if (gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORY) {
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -1,11 +1,11 @@
 buildDir "${topobjdir}/gradle/build/mobile/android/app"
 
-apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
+// apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
 apply plugin: 'com.android.application'
 apply plugin: 'checkstyle'
 
 android {
     compileSdkVersion 23
     buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
 
     defaultConfig {
@@ -198,33 +198,35 @@ dependencies {
     compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
 
+    compile 'com.swrve.sdk.android:swrve-google:4.6.1'
+
     if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
-        compile "com.android.support:mediarouter-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
-        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-cast:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        compile "com.android.support:mediarouter-v7:9.0.2"
+        compile "com.google.android.gms:play-services-basement:9.0.2"
+        compile "com.google.android.gms:play-services-base:9.0.2"
+        compile "com.google.android.gms:play-services-cast:9.0.2"
     }
 
     if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
-        compile "com.google.android.gms:play-services-ads:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        compile "com.google.android.gms:play-services-ads:9.0.2"
+        compile "com.google.android.gms:play-services-basement:9.0.2"
     }
 
     if (mozconfig.substs.MOZ_ANDROID_GCM) {
-        compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
-        compile "com.google.android.gms:play-services-measurement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
+        compile "com.google.android.gms:play-services-basement:9.0.2"
+        compile "com.google.android.gms:play-services-base:9.0.2"
+        compile "com.google.android.gms:play-services-gcm:9.0.2"
+        // compile "com.google.android.gms:play-services-measurement:9.0.2"
     }
 
     // Include LeakCanary in most gradle based builds. LeakCanary adds about 5k methods, so we disable
     // it for the (non-proguarded, non-multidex) localOld builds to allow space for other libraries.
     // Gradle based tests include the no-op version.  Mach based builds only include the no-op version
     // of this library.
     // It doesn't seem like there is a non-trivial way to be conditional on 'localOld', so instead we explicitly
     // define a version of leakcanary for every flavor:
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -17,17 +17,17 @@
 
 <!-- The bouncer APK and the main APK should define the same set of
      <permission>, <uses-permission>, and <uses-feature> elements.  This reduces
      the likelihood of permission-related surprises when installing the main APK
      on top of a pre-installed bouncer APK.  Add such shared elements in the
      fileincluded here, so that they can be referenced by both APKs. -->
 #include FennecManifest_permissions.xml.in
 
-    <application android:label="@string/moz_app_displayname"
+    <application android:label="Fennec Swrve"
                  android:icon="@drawable/icon"
                  android:logo="@drawable/logo"
                  android:name="@MOZ_ANDROID_APPLICATION_CLASS@"
                  android:hardwareAccelerated="true"
                  android:allowBackup="false"
 # The preprocessor does not yet support arbitrary parentheses, so this cannot
 # be parenthesized thus to clarify that the logical AND operator has precedence:
 #   !defined(MOZILLA_OFFICIAL) || (defined(NIGHTLY_BUILD) && defined(MOZ_DEBUG))
@@ -67,17 +67,17 @@
                   android:launchMode="singleTask"
                   android:exported="true"
                   android:theme="@style/Gecko.App" />
 
         <!-- Bug 1256615 / Bug 1268455: We published an .App alias and we need to maintain it
              forever.  If we don't, home screen shortcuts will disappear because the intent
              filter details change. -->
         <activity-alias android:name=".App"
-                        android:label="@MOZ_APP_DISPLAYNAME@"
+                        android:label="Fennec Swrve"
                         android:targetActivity="org.mozilla.gecko.LauncherActivity">
 
             <!-- android:priority ranges between -1000 and 1000.  We never want
                  another activity to usurp the MAIN action, so we ratchet our
                  priority up. -->
             <intent-filter android:priority="999">
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -426,20 +426,33 @@
 
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_services.xml.in
 #endif
 #ifdef MOZ_ANDROID_MLS_STUMBLER
 #include ../stumbler/manifests/StumblerManifest_services.xml.in
 #endif
 
+<!-- XXX -->
 #ifdef MOZ_ANDROID_GCM
 #include GcmAndroidManifest_services.xml.in
 #endif
 
+        <meta-data android:name="SWRVE_PUSH_ICON"
+                   android:resource="@drawable/icon"/>
+        <meta-data android:name="SWRVE_PUSH_ICON_MATERIAL"
+                   android:resource="@drawable/icon"/>
+        <meta-data android:name="SWRVE_PUSH_ICON_LARGE"
+                   android:resource="@drawable/icon" />
+        <meta-data android:name="SWRVE_PUSH_ACCENT_COLOR"
+                   android:resource="@android:color/black" />
+        <meta-data android:name="SWRVE_PUSH_ACTIVITY"
+                   android:value="org.mozilla.gecko.BrowserApp"/>
+        <meta-data android:name="SWRVE_PUSH_TITLE"
+                   android:value="My Push Title"/>
         <service
             android:name="org.mozilla.gecko.media.MediaManager"
             android:enabled="true"
             android:exported="false"
             android:process=":media"
             android:isolatedProcess="false">
         </service>
 
--- a/mobile/android/base/GcmAndroidManifest_services.xml.in
+++ b/mobile/android/base/GcmAndroidManifest_services.xml.in
@@ -1,29 +1,33 @@
-        <!-- Handle GCM registration updates from on-device Google Play Services. -->
-        <service
-            android:name="org.mozilla.gecko.gcm.GcmInstanceIDListenerService"
-            android:exported="false">
-            <intent-filter>
-                <action android:name="com.google.android.gms.iid.InstanceID"/>
-            </intent-filter>
-        </service>
+
+
 
-        <!-- Provided by on-device Google Play Services.  Directs inbound messages to internal listener service. -->
+
+        <!--- SWRVE -->
         <receiver
             android:name="com.google.android.gms.gcm.GcmReceiver"
             android:exported="true"
             android:permission="com.google.android.c2dm.permission.SEND">
             <intent-filter>
                 <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                 <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                 <category android:name="@ANDROID_PACKAGE_NAME@" />
             </intent-filter>
         </receiver>
 
-        <!-- Handle messages directed by the GCM receiver. -->
-        <service
-            android:name="org.mozilla.gecko.gcm.GcmMessageListenerService"
-            android:exported="false">
+        <service android:name="com.swrve.sdk.gcm.SwrveGcmIntentService" >
             <intent-filter>
                 <action android:name="com.google.android.c2dm.intent.RECEIVE" />
             </intent-filter>
         </service>
+
+        <service android:name="com.swrve.sdk.gcm.SwrveGcmInstanceIDListenerService"
+                 android:exported="false" >
+            <intent-filter>
+                <action android:name="com.google.android.gms.iid.InstanceID" />
+            </intent-filter>
+        </service>
+
+
+
+
+
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -6,16 +6,17 @@
 package org.mozilla.gecko;
 
 import android.Manifest;
 import android.annotation.TargetApi;
 import android.app.DownloadManager;
 import android.content.ContentProviderClient;
 import android.os.Environment;
 import android.os.Process;
+import android.support.annotation.CheckResult;
 import android.support.annotation.NonNull;
 
 import android.graphics.Rect;
 
 import org.json.JSONArray;
 import org.mozilla.gecko.activitystream.ActivityStream;
 import org.mozilla.gecko.adjust.AdjustBrowserAppDelegate;
 import org.mozilla.gecko.annotation.RobocopTarget;
@@ -73,16 +74,17 @@ import org.mozilla.gecko.promotion.Reade
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.reader.SavedReaderViewHelper;
 import org.mozilla.gecko.reader.ReaderModeUtils;
 import org.mozilla.gecko.reader.ReadingListHelper;
 import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.restrictions.RestrictedProfileConfiguration;
 import org.mozilla.gecko.restrictions.Restrictions;
 import org.mozilla.gecko.search.SearchEngineManager;
+import org.mozilla.gecko.swrve.SwrveBrowserAppDelegate;
 import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
 import org.mozilla.gecko.tabqueue.TabQueueHelper;
 import org.mozilla.gecko.tabqueue.TabQueuePrompt;
 import org.mozilla.gecko.tabs.TabHistoryController;
 import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
 import org.mozilla.gecko.tabs.TabHistoryFragment;
 import org.mozilla.gecko.tabs.TabHistoryPage;
 import org.mozilla.gecko.tabs.TabsPanel;
@@ -163,16 +165,20 @@ import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.ViewFlipper;
 import com.keepsafe.switchboard.AsyncConfigLoader;
 import com.keepsafe.switchboard.SwitchBoard;
+import com.swrve.sdk.Swrve;
+import com.swrve.sdk.SwrveResourceManager;
+import com.swrve.sdk.SwrveSDK;
+
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -323,17 +329,17 @@ public class BrowserApp extends GeckoApp
             new AddToHomeScreenPromotion(),
             new ScreenshotDelegate(),
             new BookmarkStateChangeDelegate(),
             new ReaderViewBookmarkPromotion(),
             new ContentNotificationsDelegate(),
             new PostUpdateHandler(),
             mTelemetryCorePingDelegate,
             new OfflineTabStatusDelegate(),
-            new AdjustBrowserAppDelegate(mTelemetryCorePingDelegate)
+            new SwrveBrowserAppDelegate()
     ));
 
     @NonNull
     private SearchEngineManager mSearchEngineManager; // Contains reference to Context - DO NOT LEAK!
 
     private boolean mHasResumed;
 
     @Override
@@ -1503,16 +1509,25 @@ public class BrowserApp extends GeckoApp
             startService(intent);
         } else {
             // Exiting, so kill our own process.
             Process.killProcess(Process.myPid());
         }
     }
 
     @Override
+    public void onLowMemory() {
+        super.onLowMemory();
+
+        for (BrowserAppDelegate delegate : delegates) {
+            delegate.onLowMemory(this);
+        }
+    }
+
+    @Override
     protected void initializeChrome() {
         super.initializeChrome();
 
         mDoorHangerPopup.setAnchor(mBrowserToolbar.getDoorHangerAnchor());
         mDoorHangerPopup.setOnVisibilityChangeListener(this);
 
         mDynamicToolbar.setLayerView(mLayerView);
         setDynamicToolbarEnabled(mDynamicToolbar.isEnabled());
@@ -3291,16 +3306,24 @@ public class BrowserApp extends GeckoApp
 
         // Action providers are available only ICS+.
         GeckoMenuItem share = (GeckoMenuItem) mMenu.findItem(R.id.share);
 
         GeckoActionProvider provider = GeckoActionProvider.getForType(GeckoActionProvider.DEFAULT_MIME_TYPE, this);
 
         share.setActionProvider(provider);
 
+        SwrveResourceManager resourceManager = SwrveSDK.getResourceManager();
+
+        String newPrivatTabLabel = resourceManager.getAttributeAsString("menu_items", "new_private_tab", null);
+        if (newPrivatTabLabel != null) {
+            GeckoMenuItem newPrivateTab = (GeckoMenuItem) mMenu.findItem(R.id.new_private_tab);
+            newPrivateTab.setTitle(newPrivatTabLabel);
+        }
+
         return true;
     }
 
     @Override
     public void openOptionsMenu() {
         hideFirstrunPager(TelemetryContract.Method.MENU);
 
         // Disable menu access (for hardware buttons) when the software menu button is inaccessible.
@@ -3612,17 +3635,24 @@ public class BrowserApp extends GeckoApp
         Tab tab = null;
         Intent intent = null;
 
         final int itemId = item.getItemId();
 
         // Track the menu action. We don't know much about the context, but we can use this to determine
         // the frequency of use for various actions.
         String extras = getResources().getResourceEntryName(itemId);
+
+        if ("new_tab".equals(extras)) {
+            SwrveSDK.event("menu.new_tab");;
+        }
+
         if (TextUtils.equals(extras, "new_private_tab")) {
+            SwrveSDK.event("menu.new_private_tab");;
+
             // Mask private browsing
             extras = "new_tab";
         }
         Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, extras);
 
         mBrowserToolbar.cancelEdit();
 
         if (itemId == R.id.bookmark) {
@@ -3635,36 +3665,42 @@ public class BrowserApp extends GeckoApp
                     extra = "bookmark";
                 }
 
                 if (item.isChecked()) {
                     Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, extra);
                     tab.removeBookmark();
                     item.setTitle(resolveBookmarkTitleID(false));
                     item.setIcon(resolveBookmarkIconID(false));
+
+                    SwrveSDK.event("menu.bookmark.create");;
                 } else {
                     Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, extra);
                     tab.addBookmark();
                     item.setTitle(resolveBookmarkTitleID(true));
                     item.setIcon(resolveBookmarkIconID(true));
+
+                    SwrveSDK.event("menu.bookmark.remove");;
                 }
             }
             return true;
         }
 
         if (itemId == R.id.share) {
             tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
                 String url = tab.getURL();
                 if (url != null) {
                     url = ReaderModeUtils.stripAboutReaderUrl(url);
 
                     // Context: Sharing via chrome list (no explicit session is active)
                     Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "menu");
 
+                    SwrveSDK.event("menu.share");
+
                     IntentHelper.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, tab.getDisplayTitle(), false);
                 }
             }
             return true;
         }
 
         if (itemId == R.id.reload) {
             tab = Tabs.getInstance().getSelectedTab();
@@ -3697,21 +3733,25 @@ public class BrowserApp extends GeckoApp
             final String url = AboutPages.getURLForBuiltinPanelType(PanelType.COMBINED_HISTORY);
             Tabs.getInstance().loadUrl(url);
             return true;
         }
 
         if (itemId == R.id.save_as_pdf) {
             Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "pdf");
             GeckoAppShell.notifyObservers("SaveAs:PDF", null);
+
+            SwrveSDK.event("menu.pdf");
+
             return true;
         }
 
         if (itemId == R.id.print) {
             Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "print");
+            SwrveSDK.event("menu.print");
             PrintHelper.printPDF(this);
             return true;
         }
 
         if (itemId == R.id.settings) {
             intent = new Intent(this, GeckoPreferences.class);
 
             // We want to know when the Settings activity returns, because
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -4,39 +4,47 @@
 
 package org.mozilla.gecko;
 
 import android.app.Application;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
+import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.Log;
 
 import com.squareup.leakcanary.LeakCanary;
 import com.squareup.leakcanary.RefWatcher;
+import com.swrve.sdk.Swrve;
+import com.swrve.sdk.SwrveSDK;
+import com.swrve.sdk.config.SwrveConfig;
+import com.swrve.sdk.gcm.ISwrvePushNotificationListener;
+import com.swrve.sdk.messaging.ISwrveCustomButtonListener;
 
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.LocalBrowserDB;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.dlc.DownloadContentService;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.lwt.LightweightTheme;
 import org.mozilla.gecko.mdns.MulticastDNSManager;
 import org.mozilla.gecko.media.AudioFocusAgent;
 import org.mozilla.gecko.notifications.NotificationClient;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.preferences.DistroSharedPrefsImport;
+import org.mozilla.gecko.swrve.SwrveBrowserAppDelegate;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.HardwareUtils;
+import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.io.File;
 import java.lang.reflect.Method;
 
 public class GeckoApplication extends Application
     implements ContextGetter {
     private static final String LOG_TAG = "GeckoApplication";
@@ -177,16 +185,52 @@ public class GeckoApplication extends Ap
         MulticastDNSManager.getInstance(context).init();
 
         GeckoService.register();
 
         EventDispatcher.getInstance().registerBackgroundThreadListener(new EventListener(),
                 "Profile:Create");
 
         super.onCreate();
+
+        SwrveConfig config = new SwrveConfig();
+        config.setLoadCachedCampaignsAndResourcesOnUIThread(false);
+        config.setSenderId("266749552179");
+
+        SwrveSDK.createInstance(this, 4307, "fnrZWneq4oj3wFUBE1GA", config);
+        SwrveSDK.setPushNotificationListener(new ISwrvePushNotificationListener() {
+            @Override
+            public void onPushNotification(Bundle bundle) {
+                Log.w("SKDBG", "RECEIVED PUSH: " + bundle.toString());
+
+
+                if (bundle.containsKey("url")) {
+                    final String url = bundle.getString("url");
+
+                    if (GeckoThread.isRunning()) {
+                        Log.w("SKDBG", "GECKO THREAD RUNNING -> OPENING DIRECTLY");
+                        Tabs.getInstance().loadUrl(url, Tabs.LOADURL_NEW_TAB);
+                    } else {
+                        SwrveBrowserAppDelegate.queueUrl(url);
+                        Log.w("SKDBG", "queued url for opening later");
+                    }
+                }
+            }
+        });
+
+        SwrveSDK.setCustomButtonListener(new ISwrveCustomButtonListener() {
+            @Override
+            public void onAction(String action) {
+                Log.w("SKDBG", "CUSTOM ACTION: " + action);
+
+                if (StringUtils.isHttpOrHttps(action) || AboutPages.isAboutPage(action)) {
+                    Tabs.getInstance().loadUrl(action, Tabs.LOADURL_NEW_TAB);
+                }
+            }
+        });
     }
 
     public void onDelayedStartup() {
         if (AppConstants.MOZ_ANDROID_GCM) {
             // TODO: only run in main process.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStream.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStream.java
@@ -6,16 +6,18 @@
 package org.mozilla.gecko.activitystream;
 
 import android.content.Context;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.text.TextUtils;
 
 import com.keepsafe.switchboard.SwitchBoard;
+import com.swrve.sdk.SwrveResourceManager;
+import com.swrve.sdk.SwrveSDK;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.Experiments;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.publicsuffix.PublicSuffix;
 
@@ -44,16 +46,21 @@ public class ActivityStream {
      */
     private static final List<String> UNDESIRED_LABELS = Arrays.asList(
             "render",
             "login",
             "edit"
     );
 
     public static boolean isEnabled(Context context) {
+        SwrveResourceManager resourceManager = SwrveSDK.getResourceManager();
+        if (resourceManager.getAttributeAsBoolean("features", "activity-stream", false)) {
+            return true;
+        }
+
         if (!isUserEligible(context)) {
             // If the user is not eligible then disable activity stream. Even if it has been
             //  enabled before.
             return false;
         }
 
         return GeckoSharedPrefs.forApp(context)
                 .getBoolean(GeckoPreferences.PREFS_ACTIVITY_STREAM, false);
--- a/mobile/android/base/java/org/mozilla/gecko/delegates/BrowserAppDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/delegates/BrowserAppDelegate.java
@@ -69,10 +69,13 @@ public abstract class BrowserAppDelegate
 
     /**
      * Called when an activity started using startActivityForResult() returns.
      *
      * Delegates should only use request and result codes declared in BrowserApp itself (as opposed
      * to declarations in the delegate), in order to avoid conflicts.
      */
     public void onActivityResult(BrowserApp browserApp, int requestCode, int resultCode, Intent data) {}
+
+
+    public void onLowMemory(BrowserApp browserApp) {}
 }
 
--- a/mobile/android/base/java/org/mozilla/gecko/push/PushService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/push/PushService.java
@@ -86,36 +86,43 @@ public class PushService implements Bund
 
     @ReflectionTarget
     public static synchronized void onCreate(Context context) {
         if (sInstance != null) {
             return;
         }
         sInstance = new PushService(context);
 
+        /*
         sInstance.registerGeckoEventListener();
         sInstance.onStartup();
+        */
     }
 
     protected final PushManager pushManager;
 
     // NB: These are not thread-safe, we're depending on these being access from the same background thread.
     private boolean isReadyPushServiceAndroidGCM = false;
     private boolean isReadyFxAccountsPush = false;
     private final List<JSONObject> pendingPushMessages;
 
     public PushService(Context context) {
+        /*
         pushManager = new PushManager(new PushState(context, "GeckoPushState.json"), new GcmTokenClient(context), new PushManager.PushClientFactory() {
             @Override
             public PushClient getPushClient(String autopushEndpoint, boolean debug) {
                 return new PushClient(autopushEndpoint);
             }
         });
 
         pendingPushMessages = new LinkedList<>();
+        */
+
+        pushManager = null;
+        pendingPushMessages = null;
     }
 
     public void onStartup() {
         Log.i(LOG_TAG, "Starting up.");
         ThreadUtils.assertOnBackgroundThread();
 
         try {
             pushManager.startup(System.currentTimeMillis());
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/swrve/SwrveBrowserAppDelegate.java
@@ -0,0 +1,88 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.swrve;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.swrve.sdk.Swrve;
+import com.swrve.sdk.SwrveSDK;
+import com.swrve.sdk.gcm.ISwrvePushNotificationListener;
+import com.swrve.sdk.messaging.ISwrveCustomButtonListener;
+
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.delegates.BrowserAppDelegate;
+import org.mozilla.gecko.mozglue.SafeIntent;
+import org.mozilla.gecko.util.ThreadUtils;
+
+/**
+ * BrowserAppDelegate implementation that forwards some activity lifecycle methods to the Swrve SDK.
+ */
+public class SwrveBrowserAppDelegate extends BrowserAppDelegate {
+    private static String queuedUrl;
+
+    public static class OpenQueuedURLDelayed extends Thread {
+        public OpenQueuedURLDelayed() {
+            super(new Runnable() {
+                @Override
+                public void run() {
+                    GeckoThread.waitOnGecko();
+
+                    Log.w("SKDBG", "QUEUED URL OPENED");
+
+                    Tabs.getInstance().loadUrl(queuedUrl, Tabs.LOADURL_NEW_TAB);
+
+                    queuedUrl = null;
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onCreate(final BrowserApp browserApp, Bundle savedInstanceState) {
+        SwrveSDK.onCreate(browserApp);
+    }
+
+    @Override
+    public void onResume(BrowserApp browserApp) {
+        SwrveSDK.onResume(browserApp);
+
+        if (queuedUrl != null) {
+            new OpenQueuedURLDelayed().start();
+        }
+
+        SwrveSDK.event("app.resume");
+    }
+
+    @Override
+    public void onPause(BrowserApp browserApp) {
+        SwrveSDK.onPause();
+
+        SwrveSDK.event("app.pause");
+    }
+
+    @Override
+    public void onDestroy(BrowserApp browserApp) {
+        SwrveSDK.onDestroy(browserApp);
+    }
+
+    @Override
+    public void onLowMemory(BrowserApp browserApp) {
+        SwrveSDK.onLowMemory();
+    }
+
+    @Override
+    public void onNewIntent(BrowserApp browserApp, SafeIntent intent) {
+        SwrveSDK.onNewIntent(intent.getUnsafe());
+    }
+
+    public static void queueUrl(String url) {
+        queuedUrl = url;
+    }
+}