Bug 1004734 - Create system notification on browser update. r=liuche,mfinkle
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Wed, 24 Feb 2016 12:16:46 -0800
changeset 324456 2d09899861de02972eb86f542841214148e71241
parent 324455 252119bf3b6d1c725895b9d6af029519ab9398f2
child 324457 5e6d66db9b05790dea3c4654ec7309bfac424b73
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersliuche, mfinkle
bugs1004734
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1004734 - Create system notification on browser update. r=liuche,mfinkle MozReview-Commit-ID: DsVhO2kagZB
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/notifications/WhatsNewReceiver.java
mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/strings.xml.in
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -249,16 +249,25 @@
         <service android:name="org.mozilla.gecko.Restarter"
                  android:exported="false"
                  android:process="@MANGLED_ANDROID_PACKAGE_NAME@.Restarter">
         </service>
 
         <receiver android:name="org.mozilla.gecko.AlarmReceiver" >
         </receiver>
 
+        <receiver
+            android:name="org.mozilla.gecko.notifications.WhatsNewReceiver"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.PACKAGE_REPLACED" />
+                <data android:scheme="package" android:path="org.mozilla.gecko" />
+            </intent-filter>
+        </receiver>
+
 #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_activities.xml.in
 #endif
 
 #if MOZ_CRASHREPORTER
   <activity android:name="org.mozilla.gecko.CrashReporter"
             android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -16,33 +16,32 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
-import org.mozilla.gecko.preferences.GeckoPreferences;
+import org.mozilla.gecko.notifications.WhatsNewReceiver;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteException;
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Browser;
 import android.util.Log;
-import android.content.SharedPreferences;
 
 public class Tabs implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     // mOrder and mTabs are always of the same cardinality, and contain the same values.
     private final CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>();
 
     // All writes to mSelectedTab must be synchronized on the Tabs instance.
@@ -814,16 +813,23 @@ public class Tabs implements GeckoEventL
      *
      * @return      the Tab if a new one was created; null otherwise
      */
     public Tab loadUrl(String url, int flags) {
         return loadUrl(url, null, -1, null, flags);
     }
 
     public Tab loadUrlWithIntentExtras(final String url, final SafeIntent intent, final int flags) {
+        // We can't directly create a listener to tell when the user taps on the "What's new"
+        // notification, so we use this intent handling as a signal that they tapped the notification.
+        if (intent.getBooleanExtra(WhatsNewReceiver.EXTRA_WHATSNEW_NOTIFICATION, false)) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.NOTIFICATION,
+                    WhatsNewReceiver.EXTRA_WHATSNEW_NOTIFICATION);
+        }
+
         // Note: we don't get the URL from the intent so the calling
         // method has the opportunity to change the URL if applicable.
         return loadUrl(url, null, -1, intent, flags);
     }
 
     public Tab loadUrl(final String url, final String searchEngine, final int parentId, final int flags) {
         return loadUrl(url, searchEngine, parentId, null, flags);
     }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/WhatsNewReceiver.java
@@ -0,0 +1,87 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.notifications;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import com.keepsafe.switchboard.SwitchBoard;
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.Locales;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.util.Experiments;
+
+import java.util.Locale;
+
+public class WhatsNewReceiver extends BroadcastReceiver {
+
+    public static final String EXTRA_WHATSNEW_NOTIFICATION = "whatsnew_notification";
+    private static final String ACTION_NOTIFICATION_CANCELLED = "notification_cancelled";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (ACTION_NOTIFICATION_CANCELLED.equals(intent.getAction())) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.NOTIFICATION, EXTRA_WHATSNEW_NOTIFICATION);
+            return;
+        }
+
+        final String dataString = intent.getDataString();
+        if (TextUtils.isEmpty(dataString) || !dataString.contains(AppConstants.ANDROID_PACKAGE_NAME)){
+            return;
+        }
+
+        if (SwitchBoard.isInExperiment(context, Experiments.WHATSNEW_NOTIFICATION)) {
+            showWhatsNewNotification(context);
+        }
+    }
+
+    private void showWhatsNewNotification(Context context) {
+        final Notification notification = new NotificationCompat.Builder(context)
+                .setContentTitle(context.getString(R.string.whatsnew_notification_title))
+                .setContentText(context.getString(R.string.whatsnew_notification_summary))
+                .setSmallIcon(R.drawable.ic_status_logo)
+                .setAutoCancel(true)
+                .setContentIntent(getContentIntent(context))
+                .setDeleteIntent(getDeleteIntent(context))
+                .build();
+
+        final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        final int notificationID = EXTRA_WHATSNEW_NOTIFICATION.hashCode();
+        notificationManager.notify(notificationID, notification);
+
+        Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.NOTIFICATION, EXTRA_WHATSNEW_NOTIFICATION);
+    }
+
+    private PendingIntent getContentIntent(Context context) {
+        final String link = context.getString(R.string.whatsnew_notification_url,
+            AppConstants.MOZ_APP_VERSION,
+            AppConstants.OS_TARGET,
+            Locales.getLanguageTag(Locale.getDefault()));
+
+        final Intent i = new Intent(Intent.ACTION_VIEW);
+        i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+        i.setData(Uri.parse(link));
+        i.putExtra(EXTRA_WHATSNEW_NOTIFICATION, true);
+
+        return PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    private PendingIntent getDeleteIntent(Context context) {
+        final Intent i = new Intent(context, WhatsNewReceiver.class);
+        i.setAction(ACTION_NOTIFICATION_CANCELLED);
+
+        return PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
@@ -20,26 +20,30 @@ import java.util.List;
  * https://github.com/mozilla-services/switchboard-experiments
  */
 public class Experiments {
     private static final String LOGTAG = "GeckoExperiments";
 
     // Display History and Bookmarks in 3-dot menu.
     public static final String BOOKMARKS_HISTORY_MENU = "bookmark-history-menu";
 
-    // Onboarding: "Features and Story"
+    // Show search mode (instead of home panels) when tapping on urlbar if there is a search term in the urlbar.
+    public static final String SEARCH_TERM = "search-term";
+
+    // Show a system notification linking to a "What's New" page on app update.
+    public static final String WHATSNEW_NOTIFICATION = "whatsnew-notification";
+
+    // Onboarding: "Features and Story". These experiments are determined
+    // on the client, they are not part of the server config.
     public static final String ONBOARDING2_A = "onboarding2-a"; // Control: Single (blue) welcome screen
     public static final String ONBOARDING2_B = "onboarding2-b"; // 4 static Feature slides
     public static final String ONBOARDING2_C = "onboarding2-c"; // 4 static + 1 clickable (Data saving) Feature slides
 
     public static final String PREF_ONBOARDING_VERSION = "onboarding_version";
 
-    // Show search mode (instead of home panels) when tapping on urlbar if there is a search term in the urlbar.
-    public static final String SEARCH_TERM = "search-term";
-
     private static volatile Boolean disabled = null;
 
     /**
      * Determines whether Switchboard is disabled by the MOZ_DISABLE_SWITCHBOARD
      * environment variable. We need to read this value from the intent string
      * extra because environment variables from our test harness aren't set
      * until Gecko is loaded, and we need to know this before then.
      *
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -773,8 +773,13 @@ just addresses the organization to follo
 
 <!-- LOCALIZATION NOTE (unsupported_sdk_version): The user installed a build of this app that does not support
      the Android version of this device. the formatS1 is replaced by the CPU ABI (e.g., ARMv7); the formatS2 is
      replaced by the Android OS version (e.g., 14)-->
 <!ENTITY unsupported_sdk_version "Sorry! This &brandShortName; won\'t work on this device (&formatS1;, &formatS2;). Please download the correct version.">
 
 <!ENTITY eol_notification_title2 "&brandShortName; will no longer update">
 <!ENTITY eol_notification_summary "Tap to learn more">
+
+<!-- LOCALIZATION NOTE (whatsnew_notification_title, whatsnew_notification_summary): These strings
+     are used for a system notification that's shown to users after the app updates. -->
+<!ENTITY whatsnew_notification_title "&brandShortName; is up to date">
+<!ENTITY whatsnew_notification_summary "Find out what\'s new in this version">
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -437,16 +437,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'menu/MenuItemSwitcherLayout.java',
     'menu/MenuPanel.java',
     'menu/MenuPopup.java',
     'menu/QuickShareBarActionView.java',
     'MotionEventInterceptor.java',
     'NotificationClient.java',
     'NotificationHandler.java',
     'NotificationHelper.java',
+    'notifications/WhatsNewReceiver.java',
     'NotificationService.java',
     'NSSBridge.java',
     'OrderedBroadcastHelper.java',
     'overlays/OverlayConstants.java',
     'overlays/service/OverlayActionService.java',
     'overlays/service/ShareData.java',
     'overlays/service/sharemethods/AddBookmark.java',
     'overlays/service/sharemethods/AddToReadingList.java',
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -606,9 +606,14 @@
 
   <string name="devtools_auth_scan_header">&devtools_auth_scan_header;</string>
 
   <string name="unsupported_sdk_version">&unsupported_sdk_version;</string>
   <string name="eol_notification_title">&eol_notification_title2;</string>
   <string name="eol_notification_summary">&eol_notification_summary;</string>
   <!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/honeycomb -->
   <string name="eol_notification_url">https://support.mozilla.org/1/mobile/&formatS1;/&formatS2;/&formatS3;/unsupported-version</string>
+
+  <string name="whatsnew_notification_title">&whatsnew_notification_title;</string>
+  <string name="whatsnew_notification_summary">&whatsnew_notification_summary;</string>
+  <!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/new-android -->
+  <string name="whatsnew_notification_url">https://support.mozilla.org/1/mobile/&formatS1;/&formatS2;/&formatS3;/new-android</string>
 </resources>