Bug 1148431 - Create UI to inform users of Tab Queue and allow them to turn on or ignore (r=mcomella)
authorMartyn Haigh <martyn.haigh@gmail.com>
Thu, 09 Apr 2015 10:55:13 +0100
changeset 238235 fc26d177eb06d433691b0f8f3e6759f9b5af080a
parent 238234 58d88105ed8e94a1076c398a7d65acef594b728b
child 238236 d9fca27fc251ff1aff9e8fbe2fbf383c33e60168
push id12278
push usermhaigh@mozilla.com
push dateThu, 09 Apr 2015 10:29:49 +0000
treeherderfx-team@fc26d177eb06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcomella
bugs1148431
milestone40.0a1
Bug 1148431 - Create UI to inform users of Tab Queue and allow them to turn on or ignore (r=mcomella)
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/BrowserApp.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/resources/drawable-hdpi/img_check.png
mobile/android/base/resources/drawable-mdpi/img_check.png
mobile/android/base/resources/drawable-xhdpi/img_check.png
mobile/android/base/resources/drawable-xxhdpi/img_check.png
mobile/android/base/resources/drawable/button_background_action_orange_round.xml
mobile/android/base/resources/drawable/button_enabled_action_orange_round.xml
mobile/android/base/resources/drawable/button_pressed_action_orange_round.xml
mobile/android/base/resources/drawable/firstrun_button_background.xml
mobile/android/base/resources/drawable/firstrun_button_enabled.xml
mobile/android/base/resources/drawable/firstrun_button_pressed.xml
mobile/android/base/resources/drawable/tab_queue_dismiss_button_foreground.xml
mobile/android/base/resources/layout-mdpi/firstrun_welcome_fragment.xml
mobile/android/base/resources/layout/firstrun_welcome_fragment.xml
mobile/android/base/resources/layout/tab_queue_prompt.xml
mobile/android/base/resources/values-large-v11/dimens.xml
mobile/android/base/resources/values/colors.xml
mobile/android/base/resources/values/dimens.xml
mobile/android/base/resources/values/themes.xml
mobile/android/base/strings.xml.in
mobile/android/base/tabqueue/TabQueueHelper.java
mobile/android/base/tabqueue/TabQueuePrompt.java
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -251,16 +251,20 @@
         </activity-alias>
 
 #ifdef MOZ_ANDROID_TAB_QUEUE
         <!-- The main reason for the Tab Queue build flag is to not mess with the VIEW intent filter
              before the rest of the plumbing is in place -->
 
         <service android:name="org.mozilla.gecko.tabqueue.TabQueueService" />
 
+        <activity android:name="org.mozilla.gecko.tabqueue.TabQueuePrompt"
+                  android:launchMode="singleTop"
+                  android:theme="@style/OverlayActivity" />
+
         <activity android:name="org.mozilla.gecko.tabqueue.TabQueueDispatcher"
                   android:label="@MOZ_APP_DISPLAYNAME@"
                   android:launchMode="singleTask"
                   android:theme="@style/TabQueueActivity">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
@@ -423,17 +427,17 @@
 
 #ifdef MOZ_ANDROID_SHARE_OVERLAY
         <!-- Share overlay activity
 
              Setting launchMode="singleTop" ensures onNewIntent is called when the Activity is
              reused. Ideally we create a new instance but Android L breaks this (bug 1137928). -->
         <activity android:name="org.mozilla.gecko.overlays.ui.ShareDialog"
                   android:label="@string/overlay_share_label"
-                  android:theme="@style/ShareOverlayActivity"
+                  android:theme="@style/OverlayActivity"
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc|locale|layoutDirection"
                   android:launchMode="singleTop"
                   android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
 
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="text/plain" />
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -46,16 +46,17 @@ import org.mozilla.gecko.mozglue.Context
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.prompts.PromptListItem;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 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;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.toolbar.BrowserToolbar;
 import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
@@ -162,16 +163,17 @@ public class BrowserApp extends GeckoApp
     public static final String GUEST_BROWSING_ARG = "--guest";
 
     private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
 
     private static final String BROWSER_SEARCH_TAG = "browser_search";
 
     // Request ID for startActivityForResult.
     private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
+    private static final int ACTIVITY_REQUEST_TAB_QUEUE = 2001;
 
     @RobocopTarget
     public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
 
     private BrowserSearch mBrowserSearch;
     private View mBrowserSearchContainer;
 
     public ViewGroup mBrowserChrome;
@@ -709,16 +711,18 @@ public class BrowserApp extends GeckoApp
         });
         mBrowserToolbar.setTabHistoryController(tabHistoryController);
 
         final String action = intent.getAction();
         if (Intent.ACTION_VIEW.equals(action)) {
             // Show the target URL immediately in the toolbar.
             mBrowserToolbar.setTitle(intent.getDataString());
 
+            showTabQueuePromptIfApplicable(intent);
+
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT);
         } else if (GuestSession.NOTIFICATION_INTENT.equals(action)) {
             GuestSession.handleIntent(this, intent);
         }
 
         if (HardwareUtils.isTablet()) {
             mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.new_tablet_tab_strip)).inflate());
         }
@@ -2422,16 +2426,21 @@ public class BrowserApp extends GeckoApp
                         Log.d(LOGTAG, "Read persisted locale " + locale);
                         if (locale == null) {
                             return;
                         }
                         onLocaleChanged(Locales.getLanguageTag(locale));
                     }
                 });
                 break;
+
+            case ACTIVITY_REQUEST_TAB_QUEUE:
+                TabQueueHelper.processTabQueuePromptResponse(resultCode, this);
+                break;
+
             default:
                 super.onActivityResult(requestCode, resultCode, data);
         }
     }
 
     private void showFirstrunPager() {
         if (mFirstrunPane == null) {
             final ViewStub firstrunPagerStub = (ViewStub) findViewById(R.id.firstrun_pager_stub);
@@ -3413,16 +3422,18 @@ public class BrowserApp extends GeckoApp
             // GeckoApp.ACTION_HOMESCREEN_SHORTCUT means we're opening a bookmark that
             // was added to Android's homescreen.
             final TelemetryContract.Method method =
                 (isViewAction ? TelemetryContract.Method.INTENT : TelemetryContract.Method.HOMESCREEN);
 
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, method);
         }
 
+        showTabQueuePromptIfApplicable(intent);
+
         super.onNewIntent(intent);
 
         if (AppConstants.MOZ_ANDROID_BEAM && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
         }
 
         // Only solicit feedback when the app has been launched from the icon shortcut.
@@ -3463,16 +3474,32 @@ public class BrowserApp extends GeckoApp
                     GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feedback:Show", null));
                 }
             }
         } finally {
             StrictMode.setThreadPolicy(savedPolicy);
         }
     }
 
+    private void showTabQueuePromptIfApplicable(final Intent intent) {
+        ThreadUtils.postToBackgroundThread(new Runnable() {
+            @Override
+            public void run() {
+                // We only want to show the prompt if the browser has been opened from an external url
+                if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE
+                                               && mInitialized
+                                               && Intent.ACTION_VIEW.equals(intent.getAction())
+                                               && TabQueueHelper.shouldShowTabQueuePrompt(BrowserApp.this)) {
+                    Intent promptIntent = new Intent(BrowserApp.this, TabQueuePrompt.class);
+                    startActivityForResult(promptIntent, ACTIVITY_REQUEST_TAB_QUEUE);
+                }
+            }
+        });
+    }
+
     @Override
     protected NotificationClient makeNotificationClient() {
         // The service is local to Fennec, so we can use it to keep
         // Fennec alive during downloads.
         return new ServiceNotificationClient(getApplicationContext());
     }
 
     private void resetFeedbackLaunchCount() {
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -189,16 +189,21 @@
 
 <!ENTITY pref_tracking_protection_title "Tracking protection">
 <!ENTITY pref_tracking_protection_summary "&brandShortName; will prevent sites from tracking you">
 <!ENTITY pref_donottrack_title "Do not track">
 <!ENTITY pref_donottrack_summary "&brandShortName; will tell sites that you do not want to be tracked">
 
 <!ENTITY tab_queue_toast_message "Open later">
 <!ENTITY tab_queue_toast_action "Open now">
+<!ENTITY tab_queue_prompt_title "Opening multiple links?">
+<!ENTITY tab_queue_prompt_text "Open them without switching to Firefox each time.">
+<!ENTITY tab_queue_prompt_tip_text "you can change this later in Settings">
+<!ENTITY tab_queue_prompt_positive_action_button "Enable">
+<!ENTITY tab_queue_prompt_negative_action_button "Not now">
 <!-- Localization note (tab_queue_notification_text_plural) : The
      formatD is replaced with the number of tabs queued.  The
      number of tabs queued is always more than one.  We can't use
      Android plural forms, sadly. See Bug #753859. -->
 <!ENTITY tab_queue_notification_text_plural "&formatD; tabs queued">
 <!-- Localization note (tab_queue_notification_title_plural) : This is the
      title of a notification; we expect more than one tab queued. -->
 <!ENTITY tab_queue_notification_title_plural "Tabs Queued">
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -418,16 +418,17 @@ gbjar.sources += [
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
     'sqlite/SQLiteBridgeException.java',
     'SuggestClient.java',
     'SurfaceBits.java',
     'Tab.java',
     'tabqueue/TabQueueDispatcher.java',
     'tabqueue/TabQueueHelper.java',
+    'tabqueue/TabQueuePrompt.java',
     'tabqueue/TabQueueService.java',
     'Tabs.java',
     'tabs/PrivateTabsPanel.java',
     'tabs/TabCurve.java',
     'tabs/TabHistoryController.java',
     'tabs/TabHistoryFragment.java',
     'tabs/TabHistoryItemRow.java',
     'tabs/TabHistoryPage.java',
new file mode 100644
index 0000000000000000000000000000000000000000..df814faba2b7c2970645db530f6257a2d96ab729
GIT binary patch
literal 954
zc$@*c14aCaP)<h;3K|Lk000e1NJLTq002k;001@!1^@s6WX0e1000AlNkl<Zc-rlo
zU5HIl6vr2$6nRl1F9;7Jn#5ojbI)E|lCO-++<VrRk_bg4Ul9+UP*Nfv4{F@bS^LEc
zkGyy>FTP41@FFRUF~<1*Sn)8aIgNYHo;l~9`>*vj-FxouuDkYH|GftCq~@S;6fvG4
ztM3r&PQ&`q@3>M5mQIJf%UE1Yte+dJfBqZ9_>ov|hrEic=NZNxfe{rHs>8X2BN-9_
z=HG1E3wcp7RD}pQ5t)QLYMB!eWbub#)l$oxNJ#o^OIu-F4Ng?BJ`2?(Kfoqqb+>%D
zWCGi(sOIRUNy3pJ#;zKis9?MgSyw6a8lAwAV7yp^(|2^6a?_Ly7i6`>AslHIw`Cd*
zF@A6c$KsJpPv!35NZ_FwoIV5hXSg628eGDmQ17e3=_^9q16dUX-XiH}oxx$;71PJ9
zAGzV0nox~!e8!zI9uC(L<6Xn}<!=G&Ad6wqHcqStI2C))&B2TjWYy(}7k}?twb7|%
zVb#L8aH7pH+*%<V7$#ZL&-6ScfA0d0d<m}LP^h**g&IPPm;N(NZ#D<snB-0?T;LLp
z&w6v5ChT;DzvaiRtfr<Jj$!?s7&k$sB38R&cAy=l099Jy#HyKofRmE&aEbM|N{&qm
z3iU4Gm{1eMk=#uuY*SvSJ6|`vzi{M^+|e=qtrTJr;)+<`Oa9^Tc1v61*vQkW?9F!x
zC$W1iht1ftV&Kgja5xsf8ur#FX(6<`>sGOgLcI!l$!DDB5{@6L=7etq@Qfosy_>TG
zuHnc?<cHqc<gJ9O4dcn;Yk3o=!Fc_5ymJMo#JJkwV5rteVRH@1Q9=G}0<{uc%Y+K0
z;maBv3ULJtl&D;oCZj_}0DbaVEr)@Y44=m0ja0*7>?s9$84Q|^!cby;GOci=96%aG
zj7Cy9Jv9eE%H|lPNz(Ni)-O{4s9W}bAdM0qS~~7Q%$yIhr~qk}+-?wKc@W@q$&D&x
zMLy#}86O4;j@)iQ)+7U{JiKE-@5O0^tV*m5VtneTL++Hr0kSSJ#mM5Vg!`O#GWA2X
zNbFU^`dl)Au%lQm%!O){tgMM)6=$>E1Xu^vELq2`$XjB3L+s6gJgC)rlZo}i{=yNU
zt$~^idD6;D3rCBC*bg|bhzqkIml8+7>SKL2m5fg$+aT|f^|?PABlzOaG>#Aa5Rc10
c*g^*M3pS0r)Cy1iH2?qr07*qoM6N<$g6Yx5Z~y=R
new file mode 100644
index 0000000000000000000000000000000000000000..fe4a03dabda5caf5cbd656404dee41172a80f3f8
GIT binary patch
literal 675
zc$@*F0$lxxP)<h;3K|Lk000e1NJLTq001xm001Ni1^@s6&qcWk0007NNkl<Zc-qyN
zJ7^VA5I{%#p(29X2x1|2v5_=NP+rL186zfsS26GHTtyHGh@gU?_(6LS8%bf|2MxP3
ztrj+7VIdZRS}1C%2!aq16XW+*FNJw;Kkx15z_fGE9cJ&nvruMOybUb$&?<hhf^UKd
zFZ*H70;mF3ynU=VZI%Aa3|4wFe4}q^73G^D^mu5jcQKTaAToYae)e#!<%cl0g7VD>
zluklf;1Ku5mT$JwtIEo|V8O3YG!3CW$>p0TQf1{`aD;;eez4x|Eb@WS`2u_1?6QJC
z(pTP`tFru=0(%~JW|ODW5Qo~u+PvTvE-90|jqnt-!w+e>RlH-BzOmwO7JAtT(G*^J
zF(=wDa(nYCR(d<jd3y-}=0FNKq8%CKkGEgs=D-S9?beA%leU!&&2YA!$79fr!E5Ul
zTBWD45-L9D&|A}Od3Nm^;V8s`6)z-A{;95}4_Xii4zbWwTKPcCQHTpGy-vJ{lZ)t3
zH{`A25lDhnnn-`0<CU311xL6&Q{@#8LsD4rkxP(1@PQwC%d*H@p<jWTgOCQRxWy&N
zv<bm?S>$a*3|VjqtkR@Qm}juiPZraFPEa5BHd>*NxymbzKqskdZM9W=mXAE{hi>49
zn-*EcdtH-PoOXX7pxXq3XWh}(X?ZtBLSDFsn4A1NA@80QZkU0*3C3|f3%<|T^HZ)E
zP!tF5)>gU;FRi?LUO>_Gj`hyBLe~>+U@{Q14a$Uj=EnLRBKW-#(pD%VtklnnpIgb>
z$RL!N5%t!x;*0-mV4@zG0jL6Q#AKnXtn|ShxBue4mHr;ck-wamHbk)Gp)LRb002ov
JPDHLkV1nVmKMeo?
new file mode 100644
index 0000000000000000000000000000000000000000..b9c6f1ad5ffee5f487e213700bb7e7126872a8bc
GIT binary patch
literal 1289
zc$@(V1@`)hP)<h;3K|Lk000e1NJLTq003YB002k`1^@s631{$Q000EgNkl<Zc-rlp
zX^0#}6o8*63LYRHAl|ped%dDYXLq_^A)Y8ByEEOd5Ji6|f+(U!B)=4Y_+JqfBxHx`
zSBQcLq9B6)5Cl;Jikj%gTqe;qF&b}qvq-VRX55|buIcIe;IY4UXXcyQs;*b>6$A!g
z73`}yo+7JW)vPuXt6%4S;_Et7)H1Li1d1Us>NK+Wk+$mZ1%uVvj%l6)fqdz7o0pPR
z-;3nF!8n^_cqIh#ldmoQ;3W4CV)di^9s~v><@gdOe{muIF-ccl0D;owyI}QQSQD0j
zHaO<w4;ZXAw>dr?0_DqhfivmLcr*n1%Xd#ZIR~q^Ah0k=cj96vzo5usgh2W7UEpd6
z%!fF-sBrmifABQ~=F8WNJ48-Nu?GTv<V(owK>(m_;tNZa?{>%UL*4Nq-vvGjbw+YC
zWO2I}`HZtIqmBsjMTUnUj*}qYll+v`6Hr%(o974lUgfK8{^a9Is4E>4o##_NF+LLH
z`x^N+ez?~1<s{0VeDxsIm9&fJ`joF39|-dOEx7kXUC}(g%%^-}bsywSV^w4S)Zodz
ze7T8E7-sooajO^c7XP9wz6bK8ZKG4j#xMT&V44`e)tP#_WmX&&<j=|17Vm~UNiE(f
z1*d&ds@pnQ+yu`^y^ZIQe;4G5&hVi{%z1yvqEkotn$@2=Q+Gg~$iLDO4;Y>wdv5H2
zvgK=wTYSkUMh$tAW;iK-cz`$cZdvO>+KtZkDL=Jw3VA{q-k=$O%i#^KmmTP+%9klO
z$*==?l4MasGyale@@W@k>Sic_DaNy;#?qI3>59q!16j4Rpr?b!Mn=BH-!(_K7trT~
z9~V4o)-+yf98`7r39H*c$a~Q(=|g-lIUOIn^q#EoGkwXIburDNTMODf;wvSVVQiFE
zou$Ypt2X(PpD^A6MN#$#lZBqFYF9zNWC2pf8=*+#MZBbIGkg*N_JO@X-R2pd<V);H
zOmsaIjjt`dgfuv}OyiJ+<ja)hOFm_I9h3oCeA~USm#2$QB+U)rkRB>u@@0xlIZ|bI
z|K00HzKgj7bG*j4d}92WY!pG6w9N8@$*K*W<j;T9V_fNR+S%<bt)3d3a5<n_#%{7|
z9DE`h4S6GVlTVC4r6#_1c)CO^&eTZ$t|V)&fMH^4P4gIHe6I#yz)mvJ)i8{-%(4UJ
zAbynPSK<m8sqB*c_o#?>SI0zGKvj@#Zda00@nD=Kqb`FA;c_ypKC7~P8ANjB3{;ht
zi4NBm->9PeUgx{PF{nCn3m~z2ref314tczWQGyt?WDJLY<m2{{y(WxIb9tX5LhlJa
z=?Q~Trehn&X{$aQ!jZpK`U7CZaoJSH?hrlgY^mkNc-rFQQU~NyzD$WyF$Z-;ss|*&
z<!R2}BnS9VcX}<MjO$9Cb~egp5d!~aR2xT<#dk^?`5WXSZ3xUq`UJ@0vjva*8Qq;Y
z6#{!d^q0EI0uEEY=~Yt%`XlG9Ip0vSesa2T5(F06ZOQ7>97g`Sww*W;0*mLS7p^)~
zGrl%JV(qKRX*?bRgY_d2<5No*`Cmxt0|L3N3vH=H`=2}9wY}bgKp{6+ec9&=FrC$x
z9|nP9kk%#QIMEj0l)IGY>NC~nBHk6rjxPTI3T{LMp*|_M00000NkvXXu0mjfRy}dM
new file mode 100644
index 0000000000000000000000000000000000000000..03ba871f61efbab70f5ffaf4662be3b96d989b0c
GIT binary patch
literal 1861
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
F{{t>Zcenrm
rename from mobile/android/base/resources/drawable/firstrun_button_background.xml
rename to mobile/android/base/resources/drawable/button_background_action_orange_round.xml
--- a/mobile/android/base/resources/drawable/firstrun_button_background.xml
+++ b/mobile/android/base/resources/drawable/button_background_action_orange_round.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/.  -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_pressed="true"
-          android:drawable="@drawable/firstrun_button_pressed" />
+          android:drawable="@drawable/button_pressed_action_orange_round" />
     <item android:state_enabled="true"
-          android:drawable="@drawable/firstrun_button_enabled" />
+          android:drawable="@drawable/button_enabled_action_orange_round" />
 </selector>
rename from mobile/android/base/resources/drawable/firstrun_button_enabled.xml
rename to mobile/android/base/resources/drawable/button_enabled_action_orange_round.xml
rename from mobile/android/base/resources/drawable/firstrun_button_pressed.xml
rename to mobile/android/base/resources/drawable/button_pressed_action_orange_round.xml
copy from mobile/android/base/resources/drawable/firstrun_button_background.xml
copy to mobile/android/base/resources/drawable/tab_queue_dismiss_button_foreground.xml
--- a/mobile/android/base/resources/drawable/firstrun_button_background.xml
+++ b/mobile/android/base/resources/drawable/tab_queue_dismiss_button_foreground.xml
@@ -1,11 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <!-- 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/.  -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+
     <item android:state_pressed="true"
-          android:drawable="@drawable/firstrun_button_pressed" />
-    <item android:state_enabled="true"
-          android:drawable="@drawable/firstrun_button_enabled" />
-</selector>
+          android:color="@color/tab_queue_dismiss_button_foreground_pressed" />
+
+    <item android:color="@color/tab_queue_dismiss_button_foreground"/>
+
+</selector>
\ No newline at end of file
--- a/mobile/android/base/resources/layout-mdpi/firstrun_welcome_fragment.xml
+++ b/mobile/android/base/resources/layout-mdpi/firstrun_welcome_fragment.xml
@@ -47,17 +47,17 @@
                       android:paddingTop="20dp"
                       android:paddingBottom="30dp"
                       android:gravity="center"
                       android:textAppearance="@style/TextAppearance.FirstrunRegular.Body"
                       android:text="@string/firstrun_welcome_subtext"/>
 
             <Button android:id="@+id/welcome_account"
                     style="@style/Widget.Firstrun.Button"
-                    android:background="@drawable/firstrun_button_background"
+                    android:background="@drawable/button_background_action_orange_round"
                     android:layout_gravity="center"
                     android:text="@string/firstrun_welcome_button_account"/>
 
             <TextView android:id="@+id/welcome_browse"
                       android:layout_width="@dimen/firstrun_content_width"
                       android:layout_height="wrap_content"
                       android:minHeight="92dp"
                       android:padding="20dp"
--- a/mobile/android/base/resources/layout/firstrun_welcome_fragment.xml
+++ b/mobile/android/base/resources/layout/firstrun_welcome_fragment.xml
@@ -47,17 +47,17 @@
                       android:paddingTop="20dp"
                       android:paddingBottom="30dp"
                       android:gravity="center"
                       android:textAppearance="@style/TextAppearance.FirstrunRegular.Body"
                       android:text="@string/firstrun_welcome_subtext"/>
 
             <Button android:id="@+id/welcome_account"
                     style="@style/Widget.Firstrun.Button"
-                    android:background="@drawable/firstrun_button_background"
+                    android:background="@drawable/button_background_action_orange_round"
                     android:layout_gravity="center"
                     android:text="@string/firstrun_welcome_button_account"/>
 
             <TextView android:id="@+id/welcome_browse"
                       android:layout_width="@dimen/firstrun_content_width"
                       android:layout_height="wrap_content"
                       android:padding="20dp"
                       android:gravity="center"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/tab_queue_prompt.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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/. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/tab_queue_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="@dimen/tab_queue_container_width"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|center"
+        android:background="@android:color/white"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="@dimen/tab_queue_content_width"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:fontFamily="sans-serif-light"
+            android:gravity="center_horizontal"
+            android:paddingTop="40dp"
+            android:text="@string/tab_queue_prompt_title"
+            android:textColor="@color/text_and_tabs_tray_grey"
+            android:textSize="20sp"
+
+            tools:text="Opening multiple links?" />
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="@dimen/tab_queue_content_width"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:lineSpacingMultiplier="1.25"
+            android:paddingTop="20dp"
+            android:text="@string/tab_queue_prompt_text"
+            android:textColor="@color/placeholder_grey"
+            android:textSize="16sp"
+
+            tools:text="Open them without switching to Firefox each time." />
+
+        <TextView
+            android:id="@+id/tip_text"
+            android:layout_width="@dimen/tab_queue_content_width"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingBottom="30dp"
+            android:paddingTop="20dp"
+            android:text="@string/tab_queue_prompt_tip_text"
+            android:textColor="@color/action_orange"
+            android:textSize="14sp"
+            android:textStyle="italic"
+
+            tools:text="you can change this later in Settings" />
+
+        <FrameLayout
+            android:id="@+id/bottom_container"
+            android:layout_width="match_parent"
+            android:layout_height="52dp"
+            android:layout_gravity="center"
+            android:layout_marginBottom="40dp">
+
+            <ImageView
+                android:id="@+id/enabled_confirmation"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:src="@drawable/img_check"
+                android:visibility="gone" />
+
+            <LinearLayout
+                android:id="@+id/button_container"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center_horizontal"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/cancel_button"
+                    style="@style/Widget.BaseButton"
+                    android:layout_width="@dimen/tab_queue_button_width"
+                    android:layout_height="match_parent"
+                    android:layout_gravity="center"
+                    android:background="@color/android:white"
+                    android:text="@string/tab_queue_prompt_negative_action_button"
+                    android:textColor="@drawable/tab_queue_dismiss_button_foreground"
+                    android:textSize="16sp"
+
+                    tools:text="Not now" />
+
+                <Button
+                    android:id="@+id/ok_button"
+                    style="@style/Widget.BaseButton"
+                    android:layout_width="@dimen/tab_queue_button_width"
+                    android:layout_height="match_parent"
+                    android:layout_gravity="center"
+                    android:background="@drawable/button_background_action_orange_round"
+                    android:text="@string/tab_queue_prompt_positive_action_button"
+                    android:textColor="@android:color/white"
+                    android:textSize="16sp"
+
+                    tools:text="Enable" />
+
+            </LinearLayout>
+
+        </FrameLayout>
+
+    </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
--- a/mobile/android/base/resources/values-large-v11/dimens.xml
+++ b/mobile/android/base/resources/values-large-v11/dimens.xml
@@ -13,9 +13,11 @@
     <dimen name="browser_toolbar_favicon_size">16dp</dimen>
 
     <dimen name="tabs_counter_size">26sp</dimen>
     <dimen name="panel_grid_view_column_width">200dp</dimen>
 
     <dimen name="reading_list_row_height">96dp</dimen>
     <dimen name="reading_list_row_padding_right">15dp</dimen>
 
+    <dimen name="tab_queue_container_width">360dp</dimen>
+
 </resources>
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -65,16 +65,21 @@
   <!-- highlight-focused on private nav button: 10% white over background_private -->
   <color name="highlight_nav_focused_pb">#FF3F423F</color>
 
   <!-- Firstrun tour-->
   <color name="firstrun_background">#99000000</color>
   <color name="firstrun_tabstrip">#1193CB</color>
   <color name="firstrun_pager_background">#16A3DF</color>
 
+  <!-- Tab Queue -->
+  <color name="tab_queue_dismiss_button_foreground">#16A3DF</color>
+  <color name="tab_queue_dismiss_button_foreground_pressed">#1193CB</color>
+  <color name="tab_queue_background">#16A3DF</color>
+
   <!--
       Application theme colors
   -->
   <!-- Default colors -->
   <color name="text_color_tertiary">#9198A1</color>
 
   <!-- Default inverse colors -->
   <color name="text_color_primary_inverse">#F5F5F5</color>
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -60,16 +60,20 @@
          commonly-used size of favicon and, performance permitting, fetch the remainder from the
          database. The largest available size is always stored in the database, regardless of this
          value.-->
     <dimen name="favicon_largest_interesting_size">32dp</dimen>
 
     <dimen name="firstrun_content_width">300dp</dimen>
     <dimen name="firstrun_min_height">180dp</dimen>
 
+    <dimen name="tab_queue_content_width">260dp</dimen>
+    <dimen name="tab_queue_button_width">148dp</dimen>
+    <dimen name="tab_queue_container_width">@dimen/match_parent</dimen>
+
     <!-- Site security icon -->
     <!-- If one of these values changes, they all should. -->
     <dimen name="site_security_bottom_margin">.5dp</dimen>
     <dimen name="new_tablet_site_security_unknown_inset_top">1dp</dimen>
     <dimen name="new_tablet_site_security_unknown_inset_bottom">-1dp</dimen>
 
     <!-- Regular page row on about:home -->
     <dimen name="page_row_height">64dp</dimen>
@@ -202,9 +206,13 @@
     <dimen name="find_in_page_text_margin_left">5dip</dimen>
     <dimen name="find_in_page_text_margin_right">12dip</dimen>
     <dimen name="find_in_page_text_padding_left">10dip</dimen>
     <dimen name="find_in_page_text_padding_right">10dip</dimen>
     <dimen name="find_in_page_status_margin_right">10dip</dimen>
     <dimen name="find_in_page_matchcase_padding">10dip</dimen>
     <dimen name="find_in_page_control_margin_top">2dip</dimen>
 
+    <!-- http://blog.danlew.net/2015/01/06/handling-android-resources-with-non-standard-formats/ -->
+    <item name="match_parent" type="dimen">-1</item>
+    <item name="wrap_content" type="dimen">-2</item>
+
 </resources>
--- a/mobile/android/base/resources/values/themes.xml
+++ b/mobile/android/base/resources/values/themes.xml
@@ -101,18 +101,18 @@
     </style>
 
     <style name="Gecko.Preferences" parent="GeckoPreferencesBase">
         <item name="floatingHintEditTextStyle">@style/FloatingHintEditText</item>
     </style>
 
     <style name="GeckoStartPane" parent="GeckoBase"/>
 
-    <!-- Make the share overlay activity appear like an overlay. -->
-    <style name="ShareOverlayActivity">
+    <!-- Make an activity appear like an overlay. -->
+    <style name="OverlayActivity">
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:backgroundDimEnabled">true</item>
 
         <!-- Set the app's title bar color in the recent app switcher.
 
              Note: We'd prefer not to show up in the recent app switcher (bug 1137928). -->
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -236,17 +236,21 @@
   <string name="pref_import_android_summary">&pref_import_android_summary;</string>
   <string name="pref_update_autodownload">&pref_update_autodownload2;</string>
   <string name="pref_update_autodownload_wifi">&pref_update_autodownload_wifi;</string>
   <string name="pref_update_autodownload_disabled">&pref_update_autodownload_never;</string>
   <string name="pref_update_autodownload_enabled">&pref_update_autodownload_always;</string>
 
   <string name="pref_tab_queue_title">&pref_tab_queue_title;</string>
   <string name="pref_tab_queue_summary">&pref_tab_queue_summary;</string>
-
+  <string name="tab_queue_prompt_title">&tab_queue_prompt_title;</string>
+  <string name="tab_queue_prompt_text">&tab_queue_prompt_text;</string>
+  <string name="tab_queue_prompt_tip_text">&tab_queue_prompt_tip_text;</string>
+  <string name="tab_queue_prompt_positive_action_button">&tab_queue_prompt_positive_action_button;</string>
+  <string name="tab_queue_prompt_negative_action_button">&tab_queue_prompt_negative_action_button;</string>
   <string name="tab_queue_toast_message">&tab_queue_toast_message;</string>
   <string name="tab_queue_toast_action">&tab_queue_toast_action;</string>
   <string name="tab_queue_notification_text_singular">&tab_queue_notification_text_singular;</string>
   <string name="tab_queue_notification_text_plural">&tab_queue_notification_text_plural;</string>
   <string name="tab_queue_notification_title_singular">&tab_queue_notification_title_singular;</string>
   <string name="tab_queue_notification_title_plural">&tab_queue_notification_title_plural;</string>
 
   <string name="pref_about_firefox">&pref_about_firefox;</string>
--- a/mobile/android/base/tabqueue/TabQueueHelper.java
+++ b/mobile/android/base/tabqueue/TabQueueHelper.java
@@ -29,16 +29,64 @@ import org.json.JSONObject;
 public class TabQueueHelper {
     private static final String LOGTAG = "Gecko" + TabQueueHelper.class.getSimpleName();
 
     public static final String FILE_NAME = "tab_queue_url_list.json";
     public static final String LOAD_URLS_ACTION = "TAB_QUEUE_LOAD_URLS_ACTION";
     public static final int TAB_QUEUE_NOTIFICATION_ID = R.id.tabQueueNotification;
 
     public static final String PREF_TAB_QUEUE_COUNT = "tab_queue_count";
+    public static final String PREF_TAB_QUEUE_LAUNCHES = "tab_queue_launches";
+    public static final String PREF_TAB_QUEUE_TIMES_PROMPT_SHOWN = "tab_queue_times_prompt_shown";
+
+    public static final int MAX_TIMES_TO_SHOW_PROMPT = 3;
+    public static final int EXTERNAL_LAUNCHES_BEFORE_SHOWING_PROMPT = 3;
+
+    // result codes for returning from the prompt
+    public static final int TAB_QUEUE_YES = 201;
+    public static final int TAB_QUEUE_NO = 202;
+
+    /**
+     * Check if we should show the tab queue prompt
+     *
+     * @param context
+     * @return true if we should display the prompt, false if not.
+     */
+    public static boolean shouldShowTabQueuePrompt(Context context) {
+        final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+
+        boolean isTabQueueEnabled = prefs.getBoolean(GeckoPreferences.PREFS_TAB_QUEUE, false);
+        int numberOfTimesTabQueuePromptSeen = prefs.getInt(PREF_TAB_QUEUE_TIMES_PROMPT_SHOWN, 0);
+
+        // Exit early if the feature is already enabled or the user has seen the
+        // prompt more than MAX_TIMES_TO_SHOW_PROMPT times.
+        if (isTabQueueEnabled || numberOfTimesTabQueuePromptSeen >= MAX_TIMES_TO_SHOW_PROMPT) {
+            return false;
+        }
+
+        final int viewActionIntentLaunches = prefs.getInt(PREF_TAB_QUEUE_LAUNCHES, 0) + 1;
+        if (viewActionIntentLaunches < EXTERNAL_LAUNCHES_BEFORE_SHOWING_PROMPT) {
+            // Allow a few external links to open before we prompt the user.
+            prefs.edit().putInt(PREF_TAB_QUEUE_LAUNCHES, viewActionIntentLaunches).apply();
+        } else if (viewActionIntentLaunches == EXTERNAL_LAUNCHES_BEFORE_SHOWING_PROMPT) {
+            // Reset to avoid repeatedly showing the prompt if the user doesn't interact with it and
+            // we get more external VIEW action intents in.
+            final SharedPreferences.Editor editor = prefs.edit();
+            editor.remove(TabQueueHelper.PREF_TAB_QUEUE_LAUNCHES);
+
+            int timesPromptShown = prefs.getInt(TabQueueHelper.PREF_TAB_QUEUE_TIMES_PROMPT_SHOWN, 0) + 1;
+            editor.putInt(TabQueueHelper.PREF_TAB_QUEUE_TIMES_PROMPT_SHOWN, timesPromptShown);
+            editor.apply();
+
+            // Show the prompt
+            return true;
+        }
+
+        return false;
+    }
 
     /**
      * Reads file and converts any content to JSON, adds passed in URL to the data and writes back to the file,
      * creating the file if it doesn't already exist.  This should not be run on the UI thread.
      *
      * @param profile
      * @param url      URL to add
      * @param filename filename to add URL to
@@ -142,9 +190,42 @@ public class TabQueueHelper {
         } catch (IllegalArgumentException e) {
             Log.e(LOGTAG, "Error deleting Tab Queue data file.", e);
         }
 
         // TODO: Use profile shared prefs when bug 1147925 gets fixed.
         final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
         prefs.edit().remove(PREF_TAB_QUEUE_COUNT).apply();
     }
+
+    public static void processTabQueuePromptResponse(int resultCode, Context context) {
+        final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+        final SharedPreferences.Editor editor = prefs.edit();
+
+        switch (resultCode) {
+            case TAB_QUEUE_YES:
+                editor.putBoolean(GeckoPreferences.PREFS_TAB_QUEUE, true);
+
+                // By making this one more than EXTERNAL_LAUNCHES_BEFORE_SHOWING_PROMPT we ensure the prompt
+                // will never show again without having to keep track of an extra pref.
+                editor.putInt(TabQueueHelper.PREF_TAB_QUEUE_LAUNCHES,
+                                     TabQueueHelper.EXTERNAL_LAUNCHES_BEFORE_SHOWING_PROMPT + 1);
+                break;
+
+            case TAB_QUEUE_NO:
+                // The user clicked the 'no' button, so let's make sure the user never sees the prompt again by
+                // maxing out the pref used to count the VIEW action intents received and times they've seen the prompt.
+
+                editor.putInt(TabQueueHelper.PREF_TAB_QUEUE_LAUNCHES,
+                                     TabQueueHelper.EXTERNAL_LAUNCHES_BEFORE_SHOWING_PROMPT + 1);
+
+                editor.putInt(TabQueueHelper.PREF_TAB_QUEUE_TIMES_PROMPT_SHOWN,
+                                     TabQueueHelper.MAX_TIMES_TO_SHOW_PROMPT + 1);
+                break;
+
+            default:
+                // We shouldn't ever get here.
+                Log.w(LOGTAG, "Unrecognized result code received from the tab queue prompt: " + resultCode);
+        }
+
+        editor.apply();
+    }
 }
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tabqueue/TabQueuePrompt.java
@@ -0,0 +1,157 @@
+/* -*- 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.tabqueue;
+
+import org.mozilla.gecko.Locales;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.animation.TransitionsTracker;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.MotionEvent;
+import android.view.View;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.animation.AnimatorSet;
+import com.nineoldandroids.animation.ObjectAnimator;
+import com.nineoldandroids.view.ViewHelper;
+
+public class TabQueuePrompt extends Locales.LocaleAwareActivity {
+    public static final String LOGTAG = "Gecko" + TabQueuePrompt.class.getSimpleName();
+
+    // Flag set during animation to prevent animation multiple-start.
+    private boolean isAnimating;
+
+    private View containerView;
+    private View buttonContainer;
+    private View enabledConfirmation;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        showTabQueueEnablePrompt();
+    }
+
+    private void showTabQueueEnablePrompt() {
+        setContentView(R.layout.tab_queue_prompt);
+
+        findViewById(R.id.ok_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                onConfirmButtonPressed();
+            }
+        });
+        findViewById(R.id.cancel_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setResult(TabQueueHelper.TAB_QUEUE_NO);
+                finish();
+            }
+        });
+
+        containerView = findViewById(R.id.tab_queue_container);
+        buttonContainer = findViewById(R.id.button_container);
+        enabledConfirmation = findViewById(R.id.enabled_confirmation);
+
+        ViewHelper.setTranslationY(containerView, 500);
+        ViewHelper.setAlpha(containerView, 0);
+
+        final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
+        translateAnimator.setDuration(400);
+
+        final Animator alphaAnimator = ObjectAnimator.ofFloat(containerView, "alpha", 1);
+        alphaAnimator.setStartDelay(200);
+        alphaAnimator.setDuration(600);
+
+        final AnimatorSet set = new AnimatorSet();
+        set.playTogether(alphaAnimator, translateAnimator);
+        set.setStartDelay(400);
+        TransitionsTracker.track(set);
+
+        set.start();
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+
+        // Don't perform an activity-dismiss animation.
+        overridePendingTransition(0, 0);
+    }
+
+    private void onConfirmButtonPressed() {
+        enabledConfirmation.setVisibility(View.VISIBLE);
+        ViewHelper.setAlpha(enabledConfirmation, 0);
+
+        final Animator buttonsAlphaAnimator = ObjectAnimator.ofFloat(buttonContainer, "alpha", 0);
+        buttonsAlphaAnimator.setDuration(300);
+
+        final Animator messagesAlphaAnimator = ObjectAnimator.ofFloat(enabledConfirmation, "alpha", 1);
+        messagesAlphaAnimator.setDuration(300);
+        messagesAlphaAnimator.setStartDelay(200);
+
+        final AnimatorSet set = new AnimatorSet();
+        set.playTogether(buttonsAlphaAnimator, messagesAlphaAnimator);
+        TransitionsTracker.track(set);
+
+        set.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+
+                new Handler().postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        slideOut();
+                        setResult(TabQueueHelper.TAB_QUEUE_YES);
+                    }
+                }, 1000);
+            }
+        });
+
+        set.start();
+    }
+
+    /**
+     * Slide the overlay down off the screen and destroy it.
+     */
+    private void slideOut() {
+        if (isAnimating) {
+            return;
+        }
+
+        isAnimating = true;
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(containerView, "translationY", containerView.getHeight());
+        animator.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                finish();
+            }
+
+        });
+        animator.start();
+    }
+
+    /**
+     * Close the dialog if back is pressed.
+     */
+    @Override
+    public void onBackPressed() {
+        slideOut();
+    }
+
+    /**
+     * Close the dialog if the anything that isn't a button is tapped.
+     */
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        slideOut();
+        return true;
+    }
+}
\ No newline at end of file