Bug 1004734 - Create system notification on browser update. r=liuche,mfinkle draft
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Wed, 24 Feb 2016 12:16:46 -0800
changeset 334755 55e4db0438a38c8b5c0b5bf078c32c0a52c82fad
parent 334587 5f9f5bacc390e2abd9bf9acbb76bd399171900e9
child 514992 ec4291666e59e09c01089e3d6fd46d68dc64cc4b
push id11634
push usermleibovic@mozilla.com
push dateFri, 26 Feb 2016 00:39:46 +0000
reviewersliuche, mfinkle
bugs1004734
milestone47.0a1
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_title "&brandShortName; no longer supports Android 3">
 <!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
@@ -436,16 +436,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_title;</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;/honeycomb</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>