Bug 774479 - Create about:feedback page, and show it to users after the app has been launched 10 times. r=mfinkle
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Sun, 26 Aug 2012 21:33:11 -0700
changeset 105565 20b009e26b4c918c3184cf8f7594f48e3517de3a
parent 105564 59f82234c8d9c584e2b2f97b81903ba2c312cd73
child 105566 f79e4c7902a12ceef3367a7b03657f05859d3c60
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersmfinkle
bugs774479
milestone17.0a1
Bug 774479 - Create about:feedback page, and show it to users after the app has been launched 10 times. r=mfinkle
mobile/android/app/mobile.js
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoApp.java
mobile/android/chrome/content/aboutFeedback.xhtml
mobile/android/chrome/jar.mn
mobile/android/components/AboutRedirector.js
mobile/android/components/MobileComponents.manifest
mobile/android/locales/en-US/chrome/aboutFeedback.dtd
mobile/android/locales/jar.mn
mobile/android/themes/core/aboutFeedback.css
mobile/android/themes/core/images/5stars.png
mobile/android/themes/core/images/about-bg-lightblue.png
mobile/android/themes/core/jar.mn
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -421,17 +421,18 @@ pref("plugins.click_to_play", true);
 // in getting the bits from the surface.
 // Bug 756253
 pref("plugins.use_placeholder", 0);
 
 // product URLs
 // The breakpad report server to link to in about:crashes
 pref("breakpad.reportURL", "http://crash-stats.mozilla.com/report/index/");
 pref("app.support.baseURL", "http://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/");
-pref("app.feedbackURL", "http://input.mozilla.com/feedback/");
+// Used to submit data to input from about:feedback
+pref("app.feedback.postURL", "http://m.input.mozilla.org/%LOCALE%/feedback");
 pref("app.privacyURL", "http://www.mozilla.com/%LOCALE%/m/privacy.html");
 pref("app.creditsURL", "http://www.mozilla.org/credits/");
 pref("app.channelURL", "http://www.mozilla.org/%LOCALE%/firefox/channel/");
 #if MOZ_UPDATE_CHANNEL == aurora
 pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%/auroranotes/");
 #else
 pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%/releasenotes/");
 #endif
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1,21 +1,28 @@
 /* -*- 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;
 
+import org.mozilla.gecko.db.BrowserContract.Combined;
+import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.util.GeckoAsyncTask;
+import org.mozilla.gecko.util.GeckoBackgroundThread;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.app.Activity;
+import android.content.SharedPreferences;
 import android.content.Intent;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
@@ -44,16 +51,19 @@ abstract public class BrowserApp extends
     private AboutHomeContent mAboutHomeContent;
 
     static Vector<MenuItem> sAddonMenuItems = new Vector<MenuItem>();
 
     private PropertyAnimator mMainLayoutAnimator;
 
     private FindInPageBar mFindInPageBar;
 
+    // We'll ask for feedback after the user launches the app this many times.
+    private static final int FEEDBACK_LAUNCH_COUNT = 10;
+
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
         switch(msg) {
             case LOCATION_CHANGE:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     String url = tab.getURL();
                     if (url.equals("about:home"))
                         showAboutHome();
@@ -183,23 +193,31 @@ abstract public class BrowserApp extends
         if (mTabsPanel != null)
             mTabsPanel.setTabsLayoutChangeListener(this);
 
         mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
 
         if (savedInstanceState != null) {
             mBrowserToolbar.setTitle(savedInstanceState.getString(SAVED_STATE_TITLE));
         }
+
+        registerEventListener("Feedback:LastUrl");
+        registerEventListener("Feedback:OpenPlayStore");
+        registerEventListener("Feedback:MaybeLater");
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         if (mAboutHomeContent != null)
             mAboutHomeContent.onDestroy();
+
+        unregisterEventListener("Feedback:LastUrl");
+        unregisterEventListener("Feedback:OpenPlayStore");
+        unregisterEventListener("Feedback:MaybeLater");
     }
 
     @Override
     public void onContentChanged() {
         super.onContentChanged();
         if (mAboutHomeContent != null)
             mAboutHomeContent.onActivityContentChanged();
     }
@@ -333,16 +351,24 @@ abstract public class BrowserApp extends
                 });
             } else if (event.equals("Menu:Remove")) {
                 final int id = message.getInt("id");
                 mMainHandler.post(new Runnable() {
                     public void run() {
                         removeAddonMenuItem(id);
                     }
                 });
+            } else if (event.equals("Feedback:OpenPlayStore")) {
+                Intent intent = new Intent(Intent.ACTION_VIEW);
+                intent.setData(Uri.parse("market://details?id=" + getPackageName()));
+                startActivity(intent);
+            } else if (event.equals("Feedback:MaybeLater")) {
+                resetFeedbackLaunchCount();
+            } else if (event.equals("Feedback:LastUrl")) {
+                getLastUrl();
             } else {
                 super.handleMessage(event, message);
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
@@ -796,9 +822,79 @@ abstract public class BrowserApp extends
                     Log.e(LOGTAG, "error building json arguments");
                 }
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("DesktopMode:Change", args.toString()));
                 return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
     }
+
+    /*
+     * If the app has been launched a certain number of times, and we haven't asked for feedback before,
+     * open a new tab with about:feedback when launching the app from the icon shortcut.
+     */ 
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        if (!Intent.ACTION_MAIN.equals(intent.getAction()))
+            return;
+
+        (new GeckoAsyncTask<Void, Void, Boolean>(mAppContext, GeckoAppShell.getHandler()) {
+            @Override
+            public synchronized Boolean doInBackground(Void... params) {
+                // Check to see how many times the app has been launched.
+                SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
+                String keyName = getPackageName() + ".feedback_launch_count";
+                int launchCount = settings.getInt(keyName, 0);
+                if (launchCount >= FEEDBACK_LAUNCH_COUNT)
+                    return false;
+
+                // Increment the launch count and store the new value.
+                launchCount++;
+                settings.edit().putInt(keyName, launchCount).commit();
+
+                // If we've reached our magic number, show the feedback page.
+                return launchCount == FEEDBACK_LAUNCH_COUNT;
+            }
+
+            @Override
+            public void onPostExecute(Boolean shouldShowFeedbackPage) {
+                if (shouldShowFeedbackPage)
+                    loadUrlInTab("about:feedback");
+            }
+        }).execute();
+    }
+
+    private void resetFeedbackLaunchCount() {
+        GeckoBackgroundThread.post(new Runnable() {
+            @Override
+            public synchronized void run() {
+                SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
+                settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).commit();
+            }
+        });
+    }
+
+    private void getLastUrl() {
+        (new GeckoAsyncTask<Void, Void, String>(mAppContext, GeckoAppShell.getHandler()) {
+            @Override
+            public synchronized String doInBackground(Void... params) {
+                // Get the most recent URL stored in browser history.
+                String url = "";
+                Cursor c = BrowserDB.getRecentHistory(getContentResolver(), 1);
+                if (c.moveToFirst()) {
+                    url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
+                }
+                c.close();
+                return url;
+            }
+
+            @Override
+            public void onPostExecute(String url) {
+                // Don't bother sending a message if there is no URL.
+                if (url.length() > 0)
+                    GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feedback:LastUrl", url));
+            }
+        }).execute();
+    }
 }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -2140,21 +2140,21 @@ abstract public class GeckoApp
         super.onDestroy();
 
         if (mBatteryReceiver != null)
             mBatteryReceiver.unregisterFor(mAppContext);
 
         ((GeckoApplication) getApplication()).removeApplicationLifecycleCallbacks(this);
     }
 
-    private void registerEventListener(String event) {
+    protected void registerEventListener(String event) {
         GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
     }
 
-    private void unregisterEventListener(String event) {
+    protected void unregisterEventListener(String event) {
         GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this);
     }
 
     // Get a temporary directory, may return null
     public static File getTempDirectory() {
         File dir = mAppContext.getExternalFilesDir("temp");
         return dir;
     }
new file mode 100644
--- /dev/null
+++ b/mobile/android/chrome/content/aboutFeedback.xhtml
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+%globalDTD;
+<!ENTITY % aboutFeedbackDTD SYSTEM "chrome://browser/locale/aboutFeedback.dtd" >
+%aboutFeedbackDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>&pageTitle;</title>
+  <meta name="viewport" content="width=device-width; user-scalable=0" />
+  <link rel="stylesheet" href="chrome://browser/skin/aboutFeedback.css" type="text/css"/>
+  <link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
+</head>
+
+<body dir="&locale.dir;" onload="init();" onunload="uninit();">
+
+  <section id="intro" active="true">
+    <h1 class="header">&intro.header;</h1>
+    <div class="message">&intro.message;</div>
+    <div class="link-box" onclick="switchSection('happy');">
+      <a>&intro.happyLink;</a>
+    </div>
+    <div class="link-box" onclick="switchSection('sad');">
+      <a>&intro.sadLink;</a>
+    </div>
+    <div class="link-box-bottom" onclick="switchSection('idea');">
+      <a>&intro.ideaLink;</a>
+    </div>
+    <div id="sumo-message" class="fine-print">&support.pre;<a id="sumo-link">&support.link;</a>&support.post;</div>
+  </section>
+
+  <section id="happy">
+    <h1 class="header">&happy.header;</h1>
+    <div class="message-box">
+      <div class="message">&happy.message;</div>
+      <div class="fine-print">&happy.finePrint;</div>
+    </div>
+    <div class="link-box-bottom" onclick="openPlayStore();">
+      <div class="stars"/>
+      <a>&happy.ratingLink;</a>
+    </div>
+    <div class="bottom-links">
+      <a onclick="maybeLater();">&happy.maybeLater;</a>
+      <a onclick="window.close();">&happy.noThanks;</a>
+    </div>
+  </section>
+
+  <section id="sad">
+    <form onsubmit="sendFeedback(event);">
+      <div class="message">&sad.message;</div>
+      <textarea class="description" placeholder="&sad.placeholder;" rows="8" required="true"/>
+      <div class="message">&sad.lastSite;</div>
+      <input id="last-url" type="url" placeholder="&sad.urlPlaceholder;"/>
+      <div class="fine-print">&feedback.privacy;</div>
+      <input class="send-feedback" type="submit" value="&feedback.send;"/>
+    </form>
+  </section>
+
+  <section id="idea">
+    <form onsubmit="sendFeedback(event);">
+      <div class="message">&idea.message;</div>
+      <textarea class="description" placeholder="&idea.placeholder;" rows="8" required="true"/>
+      <div class="fine-print">&feedback.privacy;</div>
+      <input class="send-feedback" type="submit" value="&feedback.send;"/>
+    </form>
+  </section>
+
+  <section id="thanks-sad">
+    <h1 class="header">&sad.thanksHeader;</h1>
+    <div class="message-box-bottom">
+      <div class="message">&sad.thanksMessageTop;</div>
+      <div class="message">&sad.thanksMessageBottom;</div>
+    </div>
+  </section>
+
+  <section id="thanks-idea">
+    <h1 class="header">&idea.thanksHeader;</h1>
+    <div class="message-box-bottom">
+      <div class="message">&idea.thanksMessageTop;</div>
+      <div class="message">&idea.thanksMessageBottom;</div>
+    </div>
+  </section>
+
+  <script type="application/javascript;version=1.8"><![CDATA[
+    let Cc = Components.classes;
+    let Ci = Components.interfaces;
+    let Cu = Components.utils;
+
+    Cu.import("resource://gre/modules/Services.jsm");
+
+    function dump(a) {
+      Services.console.logStringMessage(a);
+    }
+
+    function sendMessageToJava(aMessage) {
+      Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).
+                                          handleGeckoMessage(JSON.stringify(aMessage));
+    }
+
+    function init() {
+      let sumoLink = Services.urlFormatter.formatURLPref("app.support.baseURL");
+      document.getElementById("sumo-link").href = sumoLink;
+
+      window.addEventListener("popstate", function (aEvent) {
+        updateActiveSection(aEvent.state ? aEvent.state.section : "intro")
+      }, false);
+
+      // Fill "Last visited site" input with most recent history entry URL.
+      Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
+        document.getElementById("last-url").value = aData;
+      }, "Feedback:LastUrl", false);
+
+      sendMessageToJava({
+        gecko: {
+          type: "Feedback:LastUrl"
+        }
+      });
+    }
+
+    function uninit() {
+      Services.obs.removeObserver(this, "Feedback:LastUrl");
+    }
+
+    function switchSection(aSection) {
+      history.pushState({ section: aSection }, aSection);
+      updateActiveSection(aSection);
+    }
+
+    function updateActiveSection(aSection) {
+      document.querySelector("section[active]").removeAttribute("active");
+      document.getElementById(aSection).setAttribute("active", true);
+    }
+
+    function openPlayStore() {
+      sendMessageToJava({
+        gecko: {
+          type: "Feedback:OpenPlayStore"
+        }
+      });
+
+      window.close();
+    }
+
+    function maybeLater() {
+      window.close();
+
+      sendMessageToJava({
+        gecko: {
+          type: "Feedback:MaybeLater"
+        }
+      });
+    }
+
+    function sendFeedback(aEvent) {
+      // Prevent the page from reloading.
+      aEvent.preventDefault();
+
+      let section = history.state.section;
+
+      // Sanity check.
+      if (section != "sad" && section != "idea") {
+        Cu.reportError("Trying to send feedback from an invalid section: " + section);
+        return;
+      }
+
+      let sectionElement = document.getElementById(section);
+      let descriptionElement = sectionElement.querySelector(".description");
+
+      // Bail if the description value isn't valid. HTML5 form validation will take care
+      // of showing an error message for us.
+      if (!descriptionElement.validity.valid)
+        return;
+
+      let data = new FormData();
+      data.append("description", descriptionElement.value);
+
+      if (section == "sad") {
+        data.append("_type", 2);
+
+        let urlElement = document.getElementById("last-url");
+        // Bail if the URL value isn't valid. HTML5 form validation will take care
+        // of showing an error message for us.
+        if (!urlElement.validity.valid)
+          return;
+
+        // Only send a URL string if the user provided one.
+        if (urlElement.value) {
+          data.append("add_url", true);
+          data.append("url", urlElement.value);
+        }
+      } else {
+        // Otherwise we're in the "idea" section.
+        data.append("_type", 3);        
+      }
+
+      let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
+      data.append("device", sysInfo.get("device"));
+      data.append("manufacturer", sysInfo.get("manufacturer"));
+
+      let req = new XMLHttpRequest();
+      req.addEventListener("error", function() {
+        Cu.reportError("Error sending feedback to input.mozilla.org: " + req.statusText);
+      }, false);
+      req.addEventListener("abort", function() {
+        Cu.reportError("Aborted sending feedback to input.mozilla.org: " + req.statusText);
+      }, false);
+
+      let postURL = Services.urlFormatter.formatURLPref("app.feedback.postURL");
+      req.open("POST", postURL, true);
+      req.send(data);
+
+      switchSection("thanks-" + section);
+    }
+  ]]></script>
+
+</body>
+</html>
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -9,16 +9,17 @@ chrome.jar:
 
 * content/about.xhtml                  (content/about.xhtml)
   content/config.xhtml                 (content/config.xhtml)
   content/aboutAddons.xhtml            (content/aboutAddons.xhtml)
   content/aboutAddons.js               (content/aboutAddons.js)
   content/aboutCertError.xhtml         (content/aboutCertError.xhtml)
   content/aboutDownloads.xhtml         (content/aboutDownloads.xhtml)
   content/aboutDownloads.js            (content/aboutDownloads.js)
+  content/aboutFeedback.xhtml          (content/aboutFeedback.xhtml)
   content/aboutReader.html             (content/aboutReader.html)
   content/aboutReaderContent.html      (content/aboutReaderContent.html)
   content/aboutReader.js               (content/aboutReader.js)
   content/Readability.js               (content/Readability.js)
   content/JSDOMParser.js               (content/JSDOMParser.js)
   content/readerWorker.js              (content/readerWorker.js)
   content/aboutHome.xhtml              (content/aboutHome.xhtml)
 * content/aboutRights.xhtml            (content/aboutRights.xhtml)
--- a/mobile/android/components/AboutRedirector.js
+++ b/mobile/android/components/AboutRedirector.js
@@ -63,16 +63,20 @@ let modules = {
     uri: "chrome://browser/content/aboutReader.html",
     privileged: true,
     hide: true
   },
   readercontent: {
     uri: "chrome://browser/content/aboutReaderContent.html",
     privileged: false,
     hide: true
+  },
+  feedback: {
+    uri: "chrome://browser/content/aboutFeedback.xhtml",
+    privileged: true
   }
 }
 
 function AboutRedirector() {}
 AboutRedirector.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
   classID: Components.ID("{322ba47e-7047-4f71-aebf-cb7d69325cd9}"),
 
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -6,16 +6,17 @@ contract @mozilla.org/network/protocol/a
 contract @mozilla.org/network/protocol/about;1?what=empty {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=rights {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=certerror {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=home {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=apps {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=downloads {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=reader {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=readercontent {322ba47e-7047-4f71-aebf-cb7d69325cd9}
+contract @mozilla.org/network/protocol/about;1?what=feedback {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 #ifdef MOZ_SAFE_BROWSING
 contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 #endif
 
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
new file mode 100644
--- /dev/null
+++ b/mobile/android/locales/en-US/chrome/aboutFeedback.dtd
@@ -0,0 +1,49 @@
+<!-- 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/. -->
+
+<!ENTITY pageTitle                 "&brandShortName; Feedback">
+
+<!ENTITY intro.header              "Have a minute?">
+<!ENTITY intro.message             "Tell us what you think about &brandShortName; for Android so far.">
+<!ENTITY intro.happyLink           "I love it">
+<!ENTITY intro.sadLink             "I ran into some problems">
+<!ENTITY intro.ideaLink            "I have an idea">
+
+<!-- LOCALIZATION NOTE (support.pre): Include a trailing space as needed. -->
+<!-- LOCALIZATION NOTE (support.link): Avoid leading/trailing spaces, this text is a link. -->
+<!-- LOCALIZATION NOTE (support.post): Include a starting space as needed. -->
+<!ENTITY support.pre               "If you need help or have a problem with &brandShortName;, please visit ">
+<!ENTITY support.link              "&brandShortName; Support">
+<!ENTITY support.post              ".">
+
+<!ENTITY happy.header              "That's great to hear!">
+<!ENTITY happy.message             "Want to share the love by giving us a 5 star rating on Google Play?">
+<!ENTITY happy.finePrint           "It takes less than a minute and feels great.">
+<!ENTITY happy.ratingLink          "Yes, go to Google Play">
+<!ENTITY happy.maybeLater          "Maybe Later">
+<!ENTITY happy.noThanks            "No thanks">
+
+<!ENTITY sad.message               "We’re sorry that you had some problems with &brandShortName;. Please tell us what happened so that we can fix it.">
+<!ENTITY sad.placeholder           "Enter your feedback here">
+<!ENTITY sad.lastSite              "Last visited site (optional)">
+<!-- LOCALIZATION NOTE (sad.urlPlaceholder): Placeholder text that appears in "Last visited site" input box when there is no text. -->
+<!ENTITY sad.urlPlaceholder        "http://">
+<!ENTITY sad.thanksHeader          "Thanks for letting us know.">
+
+<!-- LOCALIZATION NOTE (sad.thanksMessageTop, sad.thanksMessageBottom): These two
+     strings will appear as separate paragraphs but make up one message. -->
+<!ENTITY sad.thanksMessageTop      "We’re always working to make &brandShortName; better. Rest assured that real people will look at your feedback and do their very best to resolve your issue.">
+<!ENTITY sad.thanksMessageBottom   "Or else.">
+
+<!ENTITY idea.message              "We love hearing your ideas! Please share your thoughts below. (Just the ones about &brandShortName;, please.)">
+<!ENTITY idea.placeholder          "Enter your idea here">
+<!ENTITY idea.thanksHeader         "Thanks!">
+
+<!-- LOCALIZATION NOTE (idea.thanksMessageTop, idea.thanksMessageBottom): These two
+     strings will appear as separate paragraphs but make up one message. -->
+<!ENTITY idea.thanksMessageTop     "We appreciate you taking the time to share your thoughts. We’re always working to make &brandShortName; better and contributions like yours can lead to great things.">
+<!ENTITY idea.thanksMessageBottom  "You can't see it, but we're giving you a high five right now.">
+
+<!ENTITY feedback.privacy          "For your privacy, please don't include any personally indentifiable information in your feedback.">
+<!ENTITY feedback.send             "Send Feedback">
--- a/mobile/android/locales/jar.mn
+++ b/mobile/android/locales/jar.mn
@@ -9,16 +9,17 @@
   locale/@AB_CD@/browser/about.dtd                (%chrome/about.dtd)
   locale/@AB_CD@/browser/aboutAddons.dtd          (%chrome/aboutAddons.dtd)
   locale/@AB_CD@/browser/aboutAddons.properties   (%chrome/aboutAddons.properties)
   locale/@AB_CD@/browser/aboutApps.dtd            (%chrome/aboutApps.dtd)
   locale/@AB_CD@/browser/aboutApps.properties     (%chrome/aboutApps.properties)
   locale/@AB_CD@/browser/aboutCertError.dtd       (%chrome/aboutCertError.dtd)
   locale/@AB_CD@/browser/aboutDownloads.dtd       (%chrome/aboutDownloads.dtd)
   locale/@AB_CD@/browser/aboutDownloads.properties (%chrome/aboutDownloads.properties)
+  locale/@AB_CD@/browser/aboutFeedback.dtd        (%chrome/aboutFeedback.dtd)
   locale/@AB_CD@/browser/aboutReader.properties   (%chrome/aboutReader.properties)
   locale/@AB_CD@/browser/browser.properties       (%chrome/browser.properties)
   locale/@AB_CD@/browser/config.dtd               (%chrome/config.dtd)
   locale/@AB_CD@/browser/config.properties        (%chrome/config.properties)
   locale/@AB_CD@/browser/localepicker.properties  (%chrome/localepicker.properties)
   locale/@AB_CD@/browser/aboutHome.dtd            (%chrome/aboutHome.dtd)
   locale/@AB_CD@/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/@AB_CD@/browser/notification.dtd         (%chrome/notification.dtd)
new file mode 100644
--- /dev/null
+++ b/mobile/android/themes/core/aboutFeedback.css
@@ -0,0 +1,129 @@
+/* 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/. */
+
+body {
+  -moz-text-size-adjust: none;
+  font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
+  font-size: 14px;
+  color: #222;
+  background-image: url("chrome://browser/skin/images/about-bg-lightblue.png");
+  padding: 40px 10px 10px 10px;
+}
+
+a {
+  color: #004b98;
+}
+
+section {
+  max-width: 500px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+section:not([active]) {
+  display: none;
+}
+
+.header {
+  font-size: 24px;
+  text-align: center;
+  margin-top: 20px;
+}
+
+.message-box,
+.message-box-bottom,
+.link-box,
+.link-box-bottom {
+  background-color: #e4e9ee;
+  padding: 15px;
+}
+
+.message-box,
+.link-box {
+  border-bottom: 1px solid #c8cdd4;
+}
+
+.message-box-bottom,
+.link-box-bottom {
+  border-bottom: 2px solid #c8cdd4;
+  margin-bottom: 10px;   
+}
+
+.link-box,
+.link-box-bottom {
+  text-align: center;
+}
+
+.link-box:active,
+.link-box-bottom:active {
+  background-color: #a7b0b8;
+}
+
+.message {
+  margin-bottom: 10px;
+}
+
+.fine-print {
+  font-size: 12px;
+  color: #666;
+}
+
+.stars {
+  width: 80px;
+  height: 10px;
+  margin: -20px auto 10px auto;
+  background-image: url("chrome://browser/skin/images/5stars.png");
+  background-size: 64px 10px;
+  background-position: center;
+  background-repeat: no-repeat;
+  background-color: #e4e9ee;
+}
+
+.link-box-bottom:active > .stars {
+  background-color: transparent;
+}
+
+.bottom-links {
+  text-align: center;
+  position: absolute;
+  left: 0;
+  bottom: 40px;
+  width: 100%;
+}
+
+.bottom-links > a {
+  margin: 0 25px;
+  text-decoration: underline;
+}
+
+#sumo-message {
+  position: absolute;
+  bottom: 20px;
+  color: #444;
+  -moz-padding-end: 30px;
+}
+
+.description,
+#last-url {
+  font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
+  font-size: 14px;
+  margin-bottom: 10px;
+  padding: 5px;
+  width: -moz-calc(100% - 10px);
+}
+
+.send-feedback {
+  margin-top: 10px;
+  padding: 10px;
+  font-size: 16px;
+  width: 100%;
+  background-image: -moz-linear-gradient(rgb(87,94,102), rgb(71,77,83) 90%, rgb(45,49,53));
+  border-radius: 4px;
+  border-width: 0;
+  color: #fff;
+}
+
+.send-feedback:active {
+  background-image: -moz-linear-gradient(rgb(138,143,148), rgb(127,130,135) 90%, rgb(108,111,114));
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7fc20b45011abdc8d36be4b0e5e8aa8236cfc6cc
GIT binary patch
literal 2175
zc$}S9X;f3!77j86$|zVFL_-jeG9)*UkVJw4ArNIM1PGvoklYNCiR5AugcigieIk{q
z3Zi0FKwD6RB1#dYDl$sx(;{eP5OtvlsIZuQi4R!sz18K9ch<f4oW0NY?Y+-Fd#`oB
z3kmYm*R{}v!C?ASe{!gDPE)?7a4qH6w+(w*IhjMgbSO;3hGYy02=iiz;z0yez~F$P
zAcLhyYy&-DFm*1EMu+Hu0Ys)q;KER0T;u{VOf@`q$i)n10tg}EK@LwyLQXU^ArU+l
z2^onEL<fp}KrYWeSptS72ho_x2}}YDxx*9TAtx#g2tbH|kPG-iDN#;BezZ$eu2pIj
z67dlNC6JK+4vHQag76VZKm^vs)tQL~00a)_0=QvuIKT;kK?4{RTKV9d0XHHRN5r@z
zRt8e(O~PUmL&=mCUrLFD<U){`h(gI^G8dVvi%7yj0R#d;Wdnn8RwA6G3L(UhI}4>Y
zs}{(hlqunfA)ZKxP|1vVQ4&N#B3F+f5C;aX85T-c5~WNTO3o0Y02ee$AV966rBEoy
z{xn)jQ;0!SC@2*rNtnv}VB7qMt4~u^KxH(<U<proDGWYY#7q)^LWoKxA(cBWEFOzU
z!Mjrcz}*e)jzQz_0Dwp1T`Aref)~NZ7lUyHR&D$p*9}kbC6nFJzF2nx0Qdsl1T+Ef
zhIjY!K~u08+!~iEltK(46I|u;*0?yzC%Hr)3CMs%5}HWFU#)-;t_TuIxgs&bCk%(!
z7RX@ogerxD>Vj5c1tq-wAd4aq2@oGMOyvDd1ZX@S{a+{d0o=S?0W#oDzyRJ@0&<1R
z`hT*9QU-(ipK@7MDQi%*Tq}O1u+~1HP}wsQWpkvST{4BiG%BfNFPgmnb#V+oD9W^T
zAvGH0)}pc&Do3L4WFh`>BWIxgwr!&2aigMu+Zh35_}GJt-N-3q)s(kUXTCqy&R=gV
z`a&(+OlDoHH$8Xx*@w37w!farW(b@E4BMvK7tooS?*4@F#y_KZPPzW9l;fgE%!4CT
zN5gpq+ZwO`hPYU@A<j@f)VKWQp!#^CZB28MxU1TEZ?n-A$0yCqlV#t`k%SkwTJmA9
zHd*9pKg`&1vt?bM{~nhs>${5*qFNOR+;_(WFegpHtxY{$xz>(eu9MrjL*f>@gXyL4
z?4<L|&9lY<noFDdd#yUP#&4~URGU<%cRjVl_iGksj4n3n&_}r~@q3D>4nvLGd?SXV
zeb029VmA)&p8ArxQEk$advVWw4NYT9<iOWqqsivn@wk?<^qcL|FlsvTVKp40Rkdb#
z8Sou6Tnk=BybnLGcC4I`KaxGxnWe|OJU2;s%o{ciqT?d7BX{xd-#5)ue`qGj>4Xc~
zo^59qe_O!WtGRO?ee!*L^tbt5&J|iR(@V|a;@Fz$<!1^RXPM9IvF7xrL|BDyCXEi}
z4qZ>_`QzMACfrt=fNOdC=DMhsdGlM3G*v3fHcHP1Z6*oy{M!9PwD)=Jf6OQh5nQ=o
zC5uVAdB_0KAujOLHpC`>ace^u%pX%dFd3T9O1%26@W6+IzZX~cLS}lnnA6uEE!Mqh
zZ`94mIbX|mC}N&1SGzWz)7}u99;>H@=<aKssEPXJ6csa<QetOZn8H0gnrJp6XP8Ya
z8Gc@&Huxmka5ClR&#IlDu-SVVvkMV3>&JXv*26O~JprM*6tS&Fp?!*9(a?mSqOM8j
z?77Ukf^suyKKAQyI4SA<csfLAPPCXNrsYX2!cMVEUSoQX%D3$FGe7nApRIkLEuKEC
z_I~lVLMhNyAI9IN$%_u3T=*h2yOM3RN59lBWzH2p<gVj1s+jKdZi@;j84j3fH-0#b
z`{_k6I%nW&>~-U~X<FH%JHJXc>uG<8dRc!P)*zbdwDFX74;OJR?yr^?KXCoMJWt!h
zH8z^^oQm7Mcy9xawom=%v;$DZ8RiT1Nbu+dgQy~=Q%Pv2&E1HvBvngV0~(XE9?X^8
zVB71~Z?&3qXa@NwO7p;b%Sj{gIfC4|<c)7V?oSiy5>I%hJ*5Pj{NU|%`*M!K59X;M
zBxIx2t7E%L{opY-ShEu|8xHBDd3QC=XW3VN|NJj|pQtdSMA+7>Ee#o3L5+7FXX@9^
zSE%pAzO?CUOKzg)(@ZIG*?d6^y&<TVHOR25yw*B(*gR#4-fg??&F5tkCY;x!ySB8X
z*xRhLy20r<vFZ6Aep}AL)9~AQZ?Enxb)<J5x>TThArx=YQp$cUp%Utk#ti7P8$xqt
zw8NT5;dYhBf;@MX2Q%XpMMUzSK#l`^$m~($jP%X(njdG6d>3QfakpaKpk69E+e`et
z=UBVliP@^iOC@&Uuq>uQ^@82elMR^`^;vf{d71YP3(H52CyXUs{_WuMLf%%+H8-_U
zfeW=&1hen0<2)^SXKmtneE!!TiB16-W0iKLG~?K%c7#XEBGs{a;=}qGK?g@o3uc7=
V&_i85SFHLap!x=ptGwe5{tK)?b&dc4
index 56fd38099cc5142d6a704bc02035b04fe18735ac..20660a6c846aee7fb10775998b958cacf34dcac8
GIT binary patch
literal 4684
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
--- a/mobile/android/themes/core/jar.mn
+++ b/mobile/android/themes/core/jar.mn
@@ -6,29 +6,31 @@
 
 chrome.jar:
 % skin browser classic/1.0 %skin/
   skin/aboutPage.css                        (aboutPage.css)
   skin/about.css                            (about.css)
   skin/aboutAddons.css                      (aboutAddons.css)
   skin/aboutApps.css                        (aboutApps.css)
 * skin/aboutDownloads.css                   (aboutDownloads.css)
+  skin/aboutFeedback.css                    (aboutFeedback.css)
   skin/aboutReader.css                      (aboutReader.css)
   skin/aboutReaderContent.css               (aboutReaderContent.css)
 * skin/browser.css                          (browser.css)
 * skin/content.css                          (content.css)
   skin/config.css                           (config.css)
   skin/touchcontrols.css                    (touchcontrols.css)
   skin/netError.css                         (netError.css)
 % override chrome://global/skin/about.css chrome://browser/skin/about.css
 % override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
 % override chrome://global/skin/netError.css chrome://browser/skin/netError.css
 
   skin/fonts/opensans-regular.ttf           (fonts/opensans-regular.ttf)
   skin/fonts/opensans-light.ttf             (fonts/opensans-light.ttf)
+  skin/images/5stars.png                    (images/5stars.png)
   skin/images/addons-32.png                 (images/addons-32.png)
   skin/images/arrowleft-16.png              (images/arrowleft-16.png)
   skin/images/arrowright-16.png             (images/arrowright-16.png)
   skin/images/arrowdown-16.png              (images/arrowdown-16.png)
   skin/images/checkbox_checked.png          (images/checkbox_checked.png)
   skin/images/checkbox_checked_disabled.png (images/checkbox_checked_disabled.png)
   skin/images/checkbox_checked_pressed.png  (images/checkbox_checked_pressed.png)
   skin/images/checkbox_unchecked.png          (images/checkbox_unchecked.png)