Bug 1044947 - Frontend for share handler. r=rnewman
authorChris Kitching <chriskitching@linux.com>
Mon, 25 Aug 2014 04:24:47 -0700
changeset 225684 48e03a88b6a80479e53ef7e16693d4ed54fdbc21
parent 225683 2aec90e5aed2ee7b855fceaf0559382dbc9aa9bd
child 225685 727bbb84051d21dd4778c62844f18362090f443e
push id583
push userbhearsum@mozilla.com
push dateMon, 24 Nov 2014 19:04:58 +0000
treeherdermozilla-release@c107e74250f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs1044947
milestone34.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 1044947 - Frontend for share handler. r=rnewman
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/overlays/OverlayConstants.java
mobile/android/base/overlays/ui/OverlayToastHelper.java
mobile/android/base/overlays/ui/SendTabDeviceListArrayAdapter.java
mobile/android/base/overlays/ui/SendTabList.java
mobile/android/base/overlays/ui/SendTabTargetSelectedListener.java
mobile/android/base/overlays/ui/ShareDialog.java
mobile/android/base/resources/anim/overlay_slide_down.xml
mobile/android/base/resources/anim/overlay_slide_up.xml
mobile/android/base/resources/drawable-hdpi/overlay_bookmark_icon.png
mobile/android/base/resources/drawable-hdpi/overlay_check.png
mobile/android/base/resources/drawable-hdpi/overlay_readinglist_icon.png
mobile/android/base/resources/drawable-hdpi/overlay_send_tab_icon.png
mobile/android/base/resources/drawable-mdpi/overlay_bookmark_icon.png
mobile/android/base/resources/drawable-mdpi/overlay_check.png
mobile/android/base/resources/drawable-mdpi/overlay_readinglist_icon.png
mobile/android/base/resources/drawable-mdpi/overlay_send_tab_icon.png
mobile/android/base/resources/drawable-xhdpi/overlay_bookmark_icon.png
mobile/android/base/resources/drawable-xhdpi/overlay_check.png
mobile/android/base/resources/drawable-xhdpi/overlay_readinglist_icon.png
mobile/android/base/resources/drawable-xhdpi/overlay_send_tab_icon.png
mobile/android/base/resources/drawable-xhdpi/share_overlay_background.9.png
mobile/android/base/resources/drawable-xxhdpi/overlay_bookmark_icon.png
mobile/android/base/resources/drawable-xxhdpi/overlay_check.png
mobile/android/base/resources/drawable-xxhdpi/overlay_readinglist_icon.png
mobile/android/base/resources/drawable-xxhdpi/overlay_send_tab_icon.png
mobile/android/base/resources/layout/overlay_share_dialog.xml
mobile/android/base/resources/layout/overlay_share_send_tab_button.xml
mobile/android/base/resources/layout/overlay_share_send_tab_item.xml
mobile/android/base/resources/layout/overlay_share_toast.xml
mobile/android/base/resources/values/colors.xml
mobile/android/base/resources/values/styles.xml
mobile/android/base/strings.xml.in
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -367,16 +367,31 @@
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER">
 
             <path-permission android:pathPrefix="/search_suggest_query"
                              android:readPermission="android.permission.GLOBAL_SEARCH" />
 
         </provider>
 
 #ifdef MOZ_ANDROID_SHARE_OVERLAY
+        <!-- Share overlay activity -->
+        <activity android:name="org.mozilla.gecko.overlays.ui.ShareDialog"
+                  android:label="@string/overlay_share_header"
+                  android:theme="@style/ShareOverlayActivity"
+                  android:configChanges="keyboard|keyboardHidden|mcc|mnc|locale|layoutDirection"
+                  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" />
+            </intent-filter>
+
+        </activity>
+
         <!-- Service to handle requests from overlays. -->
         <service android:name="org.mozilla.gecko.overlays.service.OverlayActionService" />
 #endif
         <!--
           Ensure that passwords provider runs in its own process. (Bug 718760.)
           Process name is per-application to avoid loading CPs from multiple
           Fennec versions into the same process. (Bug 749727.)
           Process name is a mangled version to avoid a Talos bug. (Bug 750548.)
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -85,16 +85,33 @@
 <!ENTITY pref_category_language "Language">
 <!ENTITY pref_browser_locale "Browser language">
 
 <!-- Localization note (locale_system_default) : This string indicates that
      Firefox will use the locale currently selected in Android's settings
      to display browser chrome. -->
 <!ENTITY locale_system_default "System default">
 
+<!-- Localization note (overlay_share_bookmark_btn_label) : This string is
+     used in the share overlay menu to select an action. It is the verb
+     "to bookmark", not the noun "a bookmark". -->
+<!ENTITY overlay_share_bookmark_btn_label "Bookmark">
+<!ENTITY overlay_share_reading_list_btn_label "Add to Reading List">
+<!ENTITY overlay_share_header "Send to &brandShortName;">
+<!ENTITY overlay_share_send_other "Send to other devices">
+
+<!-- Localization note (overlay_share_send_tab_btn_label) : Used on the
+     share overlay menu to represent the "Send Tab" action when the user
+     either has not set up Sync, or has no other devices to send a tab
+     to. -->
+<!ENTITY overlay_share_send_tab_btn_label "Send to another device">
+<!ENTITY overlay_share_no_url "No link found in this share">
+<!ENTITY overlay_share_retry "Try again">
+<!ENTITY overlay_share_select_device "Select device">
+
 <!ENTITY pref_category_search3 "Search">
 <!ENTITY pref_category_search_summary "Customize your search providers">
 <!ENTITY pref_category_display "Display">
 <!ENTITY pref_category_privacy_short "Privacy">
 <!ENTITY pref_category_vendor "&vendorShortName;">
 <!ENTITY pref_category_datareporting "Data choices">
 <!ENTITY pref_learn_more "Learn more">
 <!ENTITY pref_category_installed_search_engines "Installed search engines">
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -483,17 +483,21 @@ if CONFIG['MOZ_ANDROID_SHARE_OVERLAY']:
     gbjar.sources += [
         'overlays/OverlayConstants.java',
         'overlays/service/OverlayActionService.java',
         'overlays/service/sharemethods/AddBookmark.java',
         'overlays/service/sharemethods/AddToReadingList.java',
         'overlays/service/sharemethods/ParcelableClientRecord.java',
         'overlays/service/sharemethods/SendTab.java',
         'overlays/service/sharemethods/ShareMethod.java',
-        'overlays/ui/OverlayToastHelper.java'
+        'overlays/ui/OverlayToastHelper.java',
+        'overlays/ui/SendTabDeviceListArrayAdapter.java',
+        'overlays/ui/SendTabList.java',
+        'overlays/ui/SendTabTargetSelectedListener.java',
+        'overlays/ui/ShareDialog.java',
     ]
 
 gbjar.sources += sync_java_files
 gbjar.generated_sources += sync_generated_java_files
 gbjar.extra_jars = [
     'gecko-R.jar',
     'gecko-mozglue.jar',
     'gecko-util.jar',
--- a/mobile/android/base/overlays/OverlayConstants.java
+++ b/mobile/android/base/overlays/OverlayConstants.java
@@ -43,17 +43,17 @@ public class OverlayConstants {
 
     // The URL/title of the page being shared
     public static final String EXTRA_URL = "URL";
     public static final String EXTRA_TITLE = "TITLE";
 
     // The optional extra Parcelable parameters for a ShareMethod.
     public static final String EXTRA_PARAMETERS = "EXTRA";
 
-    // The extra field key used for holding one or more share method names (See above).
+    // The extra field key used for holding the ShareMethod.Type we wish to use for an operation.
     public static final String EXTRA_SHARE_METHOD = "SHARE_METHOD";
 
     /*
      * ShareMethod UI event intent constants. Broadcast by ShareMethods using LocalBroadcastManager
      * when state has changed that requires an update of any currently-displayed share UI.
      */
 
     /*
--- a/mobile/android/base/overlays/ui/OverlayToastHelper.java
+++ b/mobile/android/base/overlays/ui/OverlayToastHelper.java
@@ -24,26 +24,53 @@ import org.mozilla.gecko.R;
 public class OverlayToastHelper {
     /**
      * Show a toast indicating a failure to share.
      * @param context Context in which to inflate the toast.
      * @param failureMessage String to display in the toast.
      * @param isTransient Should a retry button be presented?
      * @param retryListener Listener to fire when the retry button is pressed.
      */
-    public static void showFailureToast(Context context, String failureMessage, boolean isTransient, View.OnClickListener retryListener) {
-        showToast(context, failureMessage, isTransient, retryListener);
+    public static void showFailureToast(Context context, String failureMessage, View.OnClickListener retryListener) {
+        showToast(context, failureMessage, false, retryListener);
     }
-    public static void showFailureToast(Context context, String failureMessage, boolean isTransient) {
-        showFailureToast(context, failureMessage, isTransient, null);
+    public static void showFailureToast(Context context, String failureMessage) {
+        showFailureToast(context, failureMessage, null);
     }
 
     /**
      * Show a toast indicating a successful share.
-     * @param successMesssage Message to show in the toast.
+     * @param successMessage Message to show in the toast.
      */
-    public static void showSuccessToast(Context context, String successMesssage) {
-        showToast(context, successMesssage, false, null);
+    public static void showSuccessToast(Context context, String successMessage) {
+        showToast(context, successMessage, true, null);
     }
 
-    private static void showToast(Context context, String message, boolean withRetry, View.OnClickListener retryListener) {
+    private static void showToast(Context context, String message, boolean success, View.OnClickListener retryListener) {
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        View layout = inflater.inflate(R.layout.overlay_share_toast, null);
+
+        TextView text = (TextView) layout.findViewById(R.id.overlay_toast_message);
+        text.setText(message);
+
+        if (retryListener == null) {
+            // Hide the retry button.
+            layout.findViewById(R.id.overlay_toast_separator).setVisibility(View.GONE);
+            layout.findViewById(R.id.overlay_toast_retry_btn).setVisibility(View.GONE);
+        } else {
+            // Set up the button to perform a retry.
+            Button retryBtn = (Button) layout.findViewById(R.id.overlay_toast_retry_btn);
+            retryBtn.setOnClickListener(retryListener);
+        }
+
+        if (!success) {
+            // Hide the happy green tick.
+            text.setCompoundDrawables(null, null, null, null);
+        }
+
+        Toast toast = new Toast(context);
+        toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.BOTTOM, 0, 0);
+        toast.setDuration(Toast.LENGTH_SHORT);
+        toast.setView(layout);
+        toast.show();
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/overlays/ui/SendTabDeviceListArrayAdapter.java
@@ -0,0 +1,178 @@
+/* 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.overlays.ui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.Assert;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.overlays.service.sharemethods.ParcelableClientRecord;
+
+import java.util.Collection;
+
+import static org.mozilla.gecko.overlays.ui.SendTabList.*;
+
+public class SendTabDeviceListArrayAdapter extends ArrayAdapter<ParcelableClientRecord> {
+    private static final String LOGTAG = "GeckoSendTabAdapter";
+
+    private State currentState;
+
+    // String to display when in a "button-like" special state. Instead of using a
+    // ParcelableClientRecord we override the rendering using this string.
+    private String dummyRecordName;
+
+    private final SendTabTargetSelectedListener listener;
+
+    private Collection<ParcelableClientRecord> records;
+
+    // The AlertDialog to show in the event the record is pressed while in the SHOW_DEVICES state.
+    // This will show the user a prompt to select a device from a longer list of devices.
+    private AlertDialog dialog;
+
+    public SendTabDeviceListArrayAdapter(Context context, SendTabTargetSelectedListener aListener, int textViewResourceId) {
+        super(context, textViewResourceId);
+
+        listener = aListener;
+
+        // We do this manually and avoid multiple notifications when doing compound operations.
+        setNotifyOnChange(false);
+    }
+
+    /**
+     * Get an array of the contents of this adapter were it in the LIST state.
+     * Useful for determining the "real" contents of the adapter.
+     */
+    public ParcelableClientRecord[] toArray() {
+        return records.toArray(new ParcelableClientRecord[records.size()]);
+    }
+
+    public void setClientRecordList(Collection<ParcelableClientRecord> clientRecordList) {
+        records = clientRecordList;
+        updateRecordList();
+    }
+
+    /**
+     * Ensure the contents of the Adapter are synchronised with the `records` field. This may not
+     * be the case if records has recently changed, or if we have experienced a state change.
+     */
+    public void updateRecordList() {
+        if (currentState != State.LIST) {
+            return;
+        }
+
+        clear();
+
+        setNotifyOnChange(false);    // So we don't notify for each add.
+        if (AppConstants.Versions.feature11Plus) {
+             addAll(records);
+        } else {
+            for (ParcelableClientRecord record : records) {
+                add(record);
+            }
+        }
+
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        final Context context = getContext();
+
+        // Reuse View objects if they exist.
+        TextView row = (TextView) convertView;
+        if (row == null) {
+            row = (TextView) View.inflate(context, R.layout.overlay_share_send_tab_item, null);
+        }
+
+        if (currentState != State.LIST) {
+            // If we're in a special "Button-like" state, use the override string and a generic icon.
+            row.setText(dummyRecordName);
+            row.setCompoundDrawablesWithIntrinsicBounds(R.drawable.overlay_send_tab_icon, 0, 0, 0);
+        }
+
+        // If we're just a button to launch the dialog, set the listener and abort.
+        if (currentState == State.SHOW_DEVICES) {
+            row.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    dialog.show();
+                }
+            });
+
+            return row;
+        }
+
+        // The remaining states delegate to the SentTabTargetSelectedListener.
+        final String listenerGUID;
+
+        ParcelableClientRecord clientRecord = getItem(position);
+        if (currentState == State.LIST) {
+            row.setText(clientRecord.name);
+            row.setCompoundDrawablesWithIntrinsicBounds(getImage(clientRecord), 0, 0, 0);
+
+            listenerGUID = clientRecord.guid;
+        } else {
+            listenerGUID = null;
+        }
+
+        row.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                listener.onSendTabTargetSelected(listenerGUID);
+            }
+        });
+
+        return row;
+    }
+
+    private static int getImage(ParcelableClientRecord record) {
+        if ("mobile".equals(record.type)) {
+            return R.drawable.sync_mobile;
+        }
+
+        return R.drawable.sync_desktop;
+    }
+
+    public void switchState(State newState) {
+        if (currentState == newState) {
+            return;
+        }
+
+        currentState = newState;
+
+        switch (newState) {
+            case LIST:
+                updateRecordList();
+                break;
+            case NONE:
+                showDummyRecord(getContext().getResources().getString(R.string.overlay_share_send_tab_btn_label));
+                break;
+            case SHOW_DEVICES:
+                showDummyRecord(getContext().getResources().getString(R.string.overlay_share_send_other));
+                break;
+            default:
+                Assert.isTrue(false, "Unexpected state transition: " + newState);
+        }
+    }
+
+    /**
+     * Set the dummy override string to the given value and clear the list.
+     */
+    private void showDummyRecord(String name) {
+        dummyRecordName = name;
+        clear();
+        add(null);
+        notifyDataSetChanged();
+    }
+
+    public void setDialog(AlertDialog aDialog) {
+        dialog = aDialog;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/overlays/ui/SendTabList.java
@@ -0,0 +1,170 @@
+/* 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.overlays.ui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import org.mozilla.gecko.Assert;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.overlays.service.sharemethods.ParcelableClientRecord;
+
+import java.util.Arrays;
+
+import static org.mozilla.gecko.overlays.ui.SendTabList.State.LIST;
+import static org.mozilla.gecko.overlays.ui.SendTabList.State.LOADING;
+import static org.mozilla.gecko.overlays.ui.SendTabList.State.NONE;
+import static org.mozilla.gecko.overlays.ui.SendTabList.State.SHOW_DEVICES;
+
+/**
+ * The SendTab button has a few different states depending on the available devices (and whether
+ * we've loaded them yet...)
+ *
+ * Initially, the view resembles a disabled button. (the LOADING state)
+ * Once state is loaded from Sync's database, we know how many devices the user may send their tab
+ * to.
+ *
+ * If there are no targets, the user was found to not have a Sync account, or their Sync account is
+ * in a state that prevents it from being able to send a tab, we enter the NONE state and display
+ * a generic button which launches an appropriate activity to fix the situation when tapped (such
+ * as the set up Sync wizard).
+ *
+ * If the number of targets does not MAX_INLINE_SYNC_TARGETS, we present a button for each of them.
+ * (the LIST state)
+ *
+ * Otherwise, we enter the SHOW_DEVICES state, in which we display a "Send to other devices" button
+ * that takes the user to a menu for selecting a target device from their complete list of many
+ * devices.
+ */
+public class SendTabList extends ListView {
+    private static final String LOGTAG = "SendTabList";
+
+    // The maximum number of target devices to show in the main list. Further devices are available
+    // from a secondary menu.
+    public static final int MAXIMUM_INLINE_ELEMENTS = 2;
+
+    private SendTabDeviceListArrayAdapter clientListAdapter;
+
+    // Listener to fire when a share target is selected (either directly or via the prompt)
+    private SendTabTargetSelectedListener listener;
+
+    private State currentState = LOADING;
+
+    /**
+     * Enum defining the states this view may occupy.
+     */
+    public enum State {
+        // State when no sync targets exist (a generic "Send to Firefox Sync" button which launches
+        // an activity to set it up)
+        NONE,
+
+        // As NONE, but disabled. Initial state. Used until we get information from Sync about what
+        // we really want.
+        LOADING,
+
+        // A list of devices to share to.
+        LIST,
+
+        // A single button prompting the user to select a device to share to.
+        SHOW_DEVICES
+    }
+
+    public SendTabList(Context context) {
+        super(context);
+    }
+
+    public SendTabList(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setAdapter(ListAdapter adapter) {
+        Assert.isTrue(adapter instanceof SendTabDeviceListArrayAdapter);
+
+        clientListAdapter = (SendTabDeviceListArrayAdapter) adapter;
+        super.setAdapter(adapter);
+    }
+
+    public void setSendTabTargetSelectedListener(SendTabTargetSelectedListener aListener) {
+        listener = aListener;
+    }
+
+    public void switchState(State state) {
+        if (state == currentState) {
+            return;
+        }
+
+        clientListAdapter.switchState(state);
+        if (state == SHOW_DEVICES) {
+            clientListAdapter.setDialog(getDialog());
+        }
+    }
+
+    public void setSyncClients(ParcelableClientRecord[] clients) {
+        if (clients == null) {
+            clients = new ParcelableClientRecord[0];
+        }
+
+        int size = clients.length;
+        if (size == 0) {
+            // Just show a button to set up sync (or whatever).
+            switchState(NONE);
+            return;
+        }
+
+        clientListAdapter.setClientRecordList(Arrays.asList(clients));
+
+        if (size <= MAXIMUM_INLINE_ELEMENTS) {
+            // Show the list of devices inline.
+            switchState(LIST);
+            return;
+        }
+
+        // Just show a button to launch the list of devices to choose one from.
+        switchState(SHOW_DEVICES);
+    }
+
+    /**
+     * Get an AlertDialog listing all devices, allowing the user to select the one they want.
+     * Used when more than MAXIMUM_INLINE_ELEMENTS devices are found (to avoid displaying them all
+     * inline and looking crazy.
+     */
+    public AlertDialog getDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+
+        final ParcelableClientRecord[] records = clientListAdapter.toArray();
+        final String[] dialogElements = new String[records.length];
+
+        for (int i = 0; i < records.length; i++) {
+            dialogElements[i] = records[i].name;
+        }
+
+        builder.setTitle(R.string.overlay_share_select_device)
+               .setItems(dialogElements, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int index) {
+                       listener.onSendTabTargetSelected(records[index].guid);
+                   }
+               });
+
+        return builder.create();
+    }
+
+    /**
+     * Prevent scrolling of this ListView.
+     */
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_MOVE) {
+            return true;
+        }
+
+        return super.dispatchTouchEvent(ev);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/overlays/ui/SendTabTargetSelectedListener.java
@@ -0,0 +1,18 @@
+/*
+ * 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.overlays.ui;
+
+/**
+ * Interface for classes that wish to listen for the selection of an element from a SendTabList.
+ */
+public interface SendTabTargetSelectedListener {
+    /**
+     * Called when a row in the SendTabList is clicked.
+     *
+     * @param targetGUID The GUID of the ClientRecord the element represents (if any, otherwise null)
+     */
+    public void onSendTabTargetSelected(String targetGUID);
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/overlays/ui/ShareDialog.java
@@ -0,0 +1,284 @@
+/* -*- 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.overlays.ui;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.mozilla.gecko.Assert;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.overlays.OverlayConstants;
+import org.mozilla.gecko.overlays.service.OverlayActionService;
+import org.mozilla.gecko.overlays.service.sharemethods.ParcelableClientRecord;
+import org.mozilla.gecko.overlays.service.sharemethods.SendTab;
+import org.mozilla.gecko.overlays.service.sharemethods.ShareMethod;
+import org.mozilla.gecko.LocaleAware;
+import org.mozilla.gecko.sync.setup.activities.WebURLFinder;
+
+/**
+ * A transparent activity that displays the share overlay.
+ */
+public class ShareDialog extends LocaleAware.LocaleAwareActivity implements SendTabTargetSelectedListener {
+    private static final String LOGTAG = "GeckoShareDialog";
+
+    private String url;
+    private String title;
+
+    // The override intent specified by SendTab (if any). See SendTab.java.
+    private Intent sendTabOverrideIntent;
+
+    // Flag set during animation to prevent animation multiple-start.
+    private boolean isAnimating;
+
+    // BroadcastReceiver to receive callbacks from ShareMethods which are changing state.
+    private final BroadcastReceiver uiEventListener = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            ShareMethod.Type originShareMethod = intent.getParcelableExtra(OverlayConstants.EXTRA_SHARE_METHOD);
+            switch (originShareMethod) {
+                case SEND_TAB:
+                    handleSendTabUIEvent(intent);
+                    break;
+                default:
+                    throw new IllegalArgumentException("UIEvent broadcast from ShareMethod that isn't thought to support such broadcasts.");
+            }
+        }
+    };
+
+    /**
+     * Called when a UI event broadcast is received from the SendTab ShareMethod.
+     */
+    protected void handleSendTabUIEvent(Intent intent) {
+        sendTabOverrideIntent = intent.getParcelableExtra(SendTab.OVERRIDE_INTENT);
+
+        SendTabList sendTabList = (SendTabList) findViewById(R.id.overlay_send_tab_btn);
+
+        ParcelableClientRecord[] clientrecords = (ParcelableClientRecord[]) intent.getParcelableArrayExtra(SendTab.EXTRA_CLIENT_RECORDS);
+        sendTabList.setSyncClients(clientrecords);
+    }
+
+    @Override
+    protected void onDestroy() {
+        // Remove the listener when the activity is destroyed: we no longer care.
+        // Note: The activity can be destroyed without onDestroy being called. However, this occurs
+        // only when the application is killed, something which also kills the registered receiver
+        // list, and the service, and everything else: so we don't care.
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(uiEventListener);
+
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().setWindowAnimations(0);
+
+        Intent intent = getIntent();
+
+        // The URL is usually hiding somewhere in the extra text. Extract it.
+        String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
+        String pageUrl = new WebURLFinder(extraText).bestWebURL();
+
+        if (TextUtils.isEmpty(pageUrl)) {
+            Log.e(LOGTAG, "Unable to process shared intent. No URL found!");
+
+            // Display toast notifying the user of failure (most likely a developer who screwed up
+            // trying to send a share intent).
+            Toast toast = Toast.makeText(this, getResources().getText(R.string.overlay_share_no_url), Toast.LENGTH_SHORT);
+            toast.show();
+
+            return;
+        }
+
+        setContentView(R.layout.overlay_share_dialog);
+
+        LocalBroadcastManager.getInstance(this).registerReceiver(uiEventListener,
+                                                                 new IntentFilter(OverlayConstants.SHARE_METHOD_UI_EVENT));
+
+        // Have the service start any initialisation work that's necessary for us to show the correct
+        // UI. The results of such work will come in via the BroadcastListener.
+        Intent serviceStartupIntent = new Intent(this, OverlayActionService.class);
+        serviceStartupIntent.setAction(OverlayConstants.ACTION_PREPARE_SHARE);
+        startService(serviceStartupIntent);
+
+        // If provided, we use the subject text to give us something nice to display.
+        // If not, we wing it with the URL.
+        // TODO: Consider polling Fennec databases to find better information to display.
+        String subjectText = intent.getStringExtra(Intent.EXTRA_SUBJECT);
+        if (subjectText != null) {
+            ((TextView) findViewById(R.id.title)).setText(subjectText);
+        }
+
+        title = subjectText;
+        url = pageUrl;
+
+        // Set the subtitle text on the view and cause it to marquee if it's too long (which it will
+        // be, since it's a URL).
+        TextView subtitleView = (TextView) findViewById(R.id.subtitle);
+        subtitleView.setText(pageUrl);
+        subtitleView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
+        subtitleView.setSingleLine(true);
+        subtitleView.setMarqueeRepeatLimit(5);
+        subtitleView.setSelected(true);
+
+        // Start the slide-up animation.
+        Animation anim = AnimationUtils.loadAnimation(this, R.anim.overlay_slide_up);
+        findViewById(R.id.sharedialog).startAnimation(anim);
+
+        // Add button event listeners.
+
+        findViewById(R.id.overlay_share_bookmark_btn).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                addBookmark();
+            }
+        });
+
+        findViewById(R.id.overlay_share_reading_list_btn).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                addToReadingList();
+            }
+        });
+
+        // Send tab.
+        SendTabList sendTabList = (SendTabList) findViewById(R.id.overlay_send_tab_btn);
+
+        // Register ourselves as both the listener and the context for the Adapter.
+        SendTabDeviceListArrayAdapter adapter = new SendTabDeviceListArrayAdapter(this, this, R.layout.sync_list_item);
+        sendTabList.setAdapter(adapter);
+        sendTabList.setSendTabTargetSelectedListener(this);
+    }
+
+    /**
+     * Helper method to get an overlay service intent populated with the data held in this dialog.
+     */
+    private Intent getServiceIntent(ShareMethod.Type method) {
+        final Intent serviceIntent = new Intent(this, OverlayActionService.class);
+        serviceIntent.setAction(OverlayConstants.ACTION_SHARE);
+
+        serviceIntent.putExtra(OverlayConstants.EXTRA_SHARE_METHOD, (Parcelable) method);
+        serviceIntent.putExtra(OverlayConstants.EXTRA_URL, url);
+        serviceIntent.putExtra(OverlayConstants.EXTRA_TITLE, title);
+
+        return serviceIntent;
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+
+        // Don't perform an activity-dismiss animation.
+        overridePendingTransition(0, 0);
+    }
+
+    /*
+     * Button handlers. Send intents to the background service responsible for processing requests
+     * on Fennec in the background. (a nice extensible mechanism for "doing stuff without properly
+     * launching Fennec").
+     */
+
+    public void sendTab(String targetGUID) {
+        // If an override intent has been set, dispatch it.
+        if (sendTabOverrideIntent != null) {
+            startActivity(sendTabOverrideIntent);
+            finish();
+            return;
+        }
+
+        // targetGUID being null with no override intent should be an impossible state.
+        Assert.isTrue(targetGUID != null);
+
+        Intent serviceIntent = getServiceIntent(ShareMethod.Type.SEND_TAB);
+
+        // Currently, only one extra parameter is necessary (the GUID of the target device).
+        Bundle extraParameters = new Bundle();
+
+        // Future: Handle multiple-selection. Bug 1061297.
+        extraParameters.putStringArray(SendTab.SEND_TAB_TARGET_DEVICES, new String[] { targetGUID });
+
+        serviceIntent.putExtra(OverlayConstants.EXTRA_PARAMETERS, extraParameters);
+
+        startService(serviceIntent);
+        slideOut();
+    }
+
+    @Override
+    public void onSendTabTargetSelected(String targetGUID) {
+        sendTab(targetGUID);
+    }
+
+    public void addToReadingList() {
+        startService(getServiceIntent(ShareMethod.Type.ADD_TO_READING_LIST));
+        slideOut();
+    }
+
+    public void addBookmark() {
+        startService(getServiceIntent(ShareMethod.Type.ADD_BOOKMARK));
+        slideOut();
+    }
+
+    /**
+     * Slide the overlay down off the screen and destroy it.
+     */
+    private void slideOut() {
+        if (isAnimating) {
+            return;
+        }
+
+        isAnimating = true;
+        Animation anim = AnimationUtils.loadAnimation(this, R.anim.overlay_slide_down);
+        findViewById(R.id.sharedialog).startAnimation(anim);
+
+        anim.setAnimationListener(new Animation.AnimationListener() {
+            @Override
+            public void onAnimationStart(Animation animation) {
+                // Unused. I can haz Miranda method?
+            }
+
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                finish();
+            }
+
+            @Override
+            public void onAnimationRepeat(Animation animation) {
+                // Unused.
+            }
+        });
+    }
+
+    /**
+     * 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;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/anim/overlay_slide_down.xml
@@ -0,0 +1,9 @@
+<?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/. -->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@android:integer/config_longAnimTime"
+    android:fromYDelta="0"
+    android:toYDelta="100%p" />
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/anim/overlay_slide_up.xml
@@ -0,0 +1,9 @@
+<?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/. -->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@android:integer/config_longAnimTime"
+    android:fromYDelta="100%p"
+    android:toYDelta="0" />
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b8536484cd5737f5316ee25397f5e5cb980c0e0b
GIT binary patch
literal 652
zc%17D@N?(olHy`uVBq!ia0vp^+91rq3?#2LH+}+AnE^f_u0T32F+DCRJw7=Dh=7cc
z=s2&CNEH>8_#}{cY+`C0m=Tkh8XcD!9+w=K1XKwW$%so%2dRN-Plu?FOG=AQOpZ+g
zD@aO@N=T1M02%&q!XZ|mWlAMMe!&dB_s;$(<zr`jG2iyzHUl})_usB&uL(c8e{xTP
zQb)rybywHf;!_zI7&myjIEGl9?w#^-R+9mb>q6ZSGtuv?Znp3LPcY^^#1Zn~ALFOF
zYUY}`4<_!N>MpGl%(dk71)k+Og4H75cCgf5da<*imcjl&vfSNw4(w88w<E7DwP97C
zUc<MQb4BQepb2Nr{$P;V^?Zs-yyXAAN0vUBAH;FxQyB9@m1?%jU-p|8{4-nqreLp%
z!>^CwD$`#p-u||-Jz&wFoA2WF?r(Z@aSg}!FCitCTvfN)9i5iX^;=x<J6^vhXz$`+
zp*Pm6qJFkTlxlx7TKY%S=k-)`;XRB`tAt{wA2=IoX{A4#v*dA=RhM&hVcMhh8E>L?
z-RSS%{I}^=vHn%Jt**y}y0eT8>c4Kf9{74DqY}RqV_LyKf6FK8m2>&sjSel$yH^yp
zXdh3RO~C`Zu6vDVE94t5R4==BFXP!>cIW)9St4tX%v|5S<7@Do`>o0O$4zIHPuH9f
zKf9GJ>wfAwmEc?f)$;Gn8uvxx8~)8M3b}Mr@?ro_Am=(iFNMEcqP>EihEtf<wuY?R
z@z*fPrE_cJt;4Zy-)z@4x$XWa)_vyfW1UXH^J?jj^xw80JyBtuoa<wF@8>E5x9v;o
Z81)`n?ckbNxD6Q744$rjF6*2UngHo;AU^;A
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..19a1374fee79eae7bc9fa8f3073a4492e60a9c54
GIT binary patch
literal 391
zc%17D@N?(olHy`uVBq!ia0vp^+CZ$p!3-q#xD*-!sqg@w5LX}_x7JN7PX<WFtpOtt
z!!>M~UEErC2sd_(Yy4Wb_%%S}3Q-fg$|YuvYwQ}AxK(aZtDGX2*@JX})x@m@Dt2f6
z@z5A(v`k5mUogWTrmvk-uZ8nHoY|lI=5S+avcH{+p^Es9IqT<@FP?uGsP3?*i(`nz
z>8Y1*3N;z<v_8C$BtETaQ}(_8mZeMl9Cjc1|7-S3SO12IVV8IhtU4Ut`s{+xeNl6<
zuN6rb{QLX^KWS>&9nJ2#re5{4nagZ#qp$aenaf_C72n(Pa*e=7S)nb=_mjTs=Xi8+
zm~z!l<+yD3Wma@ql&eO>k~d$xr58lmsFZdcV)ieMPGWXT+;S-RQ1-N-MLhw$ja$wH
zWo`0!`mU@n(yQ~Wi|WMy-@*mTOQfUOmMT_TIoK7u&#KkC7`^kHz)Mb@CNq99$3M4$
P-eK@`^>bP0l+XkKE)t;s
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bac00e61ee9c42c641603d3fd02c090eb25a6e9f
GIT binary patch
literal 340
zc%17D@N?(olHy`uVBq!ia0vp^+CZ$t!3-on_FS+6Qr-bRA+A6=E-51}DLp76B0eb{
z#7+c~>2b+m5`vO5Vw2K<;<1V8F)_)pu?Z0I_~dj3Fo;b~j!({TEm*uB$dM=s@(X5o
zb(CxRo}MRn)}{RX9O&(=EOv201rwu=^tlshKs5_IT^vI!PQSgfQ^-k?r}d%mp|YL7
zv+n)(6qT0rop{b^zvQel@eHm9<}EROlDDT}=jrAxCye*hyxO4=%rA6w-c&`$H}iC*
z_Vg@_OOBD?-MaRV!NN^jyc^f%zFfPfW@YG&+yCwV{J4E4xBJjWrU%LbPtRJ4%cXlq
zMSuOcM>UmCsI~l+UT2c%LBmehuAcJOB1?)cb2VS;exsiA>|X2!En|`Lk7unFW|ow!
P1NqL=)z4*}Q$iB}j+2R9
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3f5700d47451225022adbd0be1fbc56fd1108307
GIT binary patch
literal 325
zc%17D@N?(olHy`uVBq!ia0vp^+CZ$t!3-on_FS+6QZ@lTA+A6=E-4*Cf>3fsTw;1m
zVj7g2lo6Yd0wg066XTN8ViVG16Vn_V9701w<C8OTuAgKF>J%;s@(X78$+Tn5f((a`
zr|)ZBk)7;o%EBouXO{((Uh3)M7-Dhy?G<mnW&<AAi_cY_oZf!#f1EcLb5l#<(pAdu
zG@6xNJ5!Q#HyqhMQFhhVHK)S7t-P;p+I08Py5s*2=KZ~=W;dlP!#!_9hVS)+i(zZF
zioVwExqMsTYMIitTMlcsO^en(`|RI_7w>c!RqOZ;mdq9{Ke;Dq*0*}LC$gb)xDT`~
zJln$=zvaj0yZ@Zp&MYrF5UPJ)KJ~r0$Ju*dXD0cb6F1q**kPwt*0R+G6g&)`u6{1-
HoD!M<3~Ys~
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c9489e49e2c49527849c2c9c24ef3ea561c5b166
GIT binary patch
literal 477
zc%17D@N?(olHy`uVBq!ia0vp^G9b*s3?yAI>n{UR2?0JKu0T32DIJV5A`_FsW0OGa
z#Pr~Z6dfJwxa2e-H#Q+HJ~<;UIXyNpJt!nHHZc{*28k!90Ll2IjOc{a`1mv+BOa&*
zOae`aO$O=O6FK`2&<^>MAirRS_s71=h@C6tW17+C{bx}Qm&5&=I(1>D$}e^$ZJ!W&
z#nQo=fq{|N)5S4F<9zAF@JUA$cw7W;STujGIP`A!f8Fw#0SVLH<4bIp<TZL}Up5QA
z|BA6cKZk9`euwsJJBp`CUK1^TcgrB`sz>oLuY=l8Cw^iP^H?9EFXFy?TY=MODZO<W
zo4;?VUdzfbnSHJ_gUj{rK`+nWjK8v3?_Y5=dqlgY`+TbdZ%;`?E>XVjyE1adC*{-X
zDRr|rw%NSNnONR(aKY~>U!{y|Ty7V>7A{+yy*S2t+6u8<rm`s?11oY)X&n~0+OSe+
zhvU3AjL}TdPqzMA9iLHtf2s5F{)b{=*LUX^Us;vI<M7-l^l@2Yw^PC%ekq^L3FV2_
zJH#F;)~Ua^w8K)t`{%xwttK_8%hguZe?Mtw{hvXTL3rC#rl&K3VaVX=>gTe~DWM4f
D7skex
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a130d78f21582b244a02418dc2972941293e0bf5
GIT binary patch
literal 308
zc%17D@N?(olHy`uVBq!ia0vp^GC(ZK!3-pm?$qT0DenNE5LX}_x7ICYl?#xJUhNDd
z<JN$<(Lj+kZhmtO<JY(W5s)1Zlv?W+y9S6{W7fFDEOv}r>l(MlHGYk2=qi`^weEXe
zR6(Xolmz(&GdxrMpz->#ss0?cn<rL%+t=cvAl{K5$^TxhP6eo@!PCVtMC1I@(~)A$
z1{@B7LOruiE-Xv<ubu0al^giy<!`2UjD4FqYXjC8x|pzC_4;>&!@uX!y*F7m{w6<^
z`n>4xQ=?gaGgh~EPJj3>+qS@C>&ggi9;Lmf|DH>b^LQflBSe0i&&IW?n#WlGr;4R)
rzZzwBv8SuhY~LHE<F<RGm)bJA*$OpZ->IVmbPI#0tDnm{r-UW|*lBva
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6c42af75e4cfb724526d6ca1478f1d9e675ed200
GIT binary patch
literal 336
zc%17D@N?(olHy`uVBq!ia0vp^GC(ZO!3-qzMee5pDZc=p5LX}_my{8ooF0>q5toz>
zL@^1eKyGXzkPQ-viBFA<O@m0qC8h(V;*!(jlYl5KIw94?B@Jjod@@KSki5&*+79GP
zmIV0)Gb}pb@a2z5Q{C=Ks!~^bl3qOB=w`^ycszFk?~`RfMKe5I978nDuRVK_uUUbo
z_2HtWF9ZVS<n8(2-Dhf&skA%dQF-~F;Kr7v8)tv)zPTpH=Ya1T{k;OOrZ42&Hjh0r
zThmlZ;PeD>0XO$`>@i72J?r%f^?G()6t><KSh9Hb%=gD~Zf=%7`rlCb^?x(IGxvX`
z%{ap0FuA*by+qa)*G^CU==G*o4j=GL-fX?*>f22(S<dErd=TF2_?v-WSY*3D=k^Mq
P8yP%Z{an^LB{Ts5F;0ZH
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..24a8aa66b0eef273def2b7ffa1ffcfdd14f97636
GIT binary patch
literal 307
zc%17D@N?(olHy`uVBq!ia0vp^GC(ZO!3-qzMee5pDc1m>5LX}_pOhX4LK#3ZIw3VK
z8N`T7NQ;e21q*_>u}P^>G08xNgM)8eGDv-NY$`}&Vp@E1hV9+9Ye0j<N`m}?8MYi=
zG|S*~Z<N~W#|18;KUr@z%dhi`@ti9QRMGC~;uxZF{^%u7zQYOvE*B5+2|PRb?zg<D
zhMU)hhPC&<Z4a5vrnJ~Fv2k)5%Llb2<u(peuAVpkuBmTL#OgjSp7BEHj)K<RRWi9n
zdpj(|17}wDq-~5`VYJ9?f2qmE?@rp{9%8#Z9nP?`$4vNlbuw4RoWoP@-<0p3m$d)>
kNx^cPe6<%P5&h4Y1w#d2q{|;z33L*Jr>mdKI;Vst02V`Z4gdfE
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4840c7298a157c7a5cc2f6cd6262ab5cdcada750
GIT binary patch
literal 755
zc%17D@N?(olHy`uVBq!ia0vp^79h;Q3?%1AoYnzS2?0JKu0T3AF*PnZBOo|BE;&6e
z35YU)WN1u+yL(VnLK=h%LQvWCm?WTJI!G5-aXd^Mf?|`>fG9d4B`z^7J~`tZb9Fh;
z4*8NGzhDNVKb&{JyonWJk>5Ds>B=kZ9;G(__MMoUy1h0aOC!>}=kgK;1}0@s7sn8f
z<8woAZfjQHDRE>Ky}m2^=l}hCR398;oMO!%>!aT2Y4r7sN5Gz{{|<-iX56i`WBzgO
zh?4+!c$^eR(gZ7^Vzr68KNfs>9Pq2UbAxc<v!hbwiOzxES9oPRl<)Z6D=AT!wqW^{
zZ8hgtFKl)<e|`N3U)r-7^Ur18vQj;6eD3qy<z+Lr1zmh8xNyGD9*?3N)&FLy`*zMw
ziGH?d%4Xk_A7`A()bFmm*-^A(m*lUG*b8^Wez{!X_toXUCuSHd`0)0g)g9f}J|FA<
zW#JH6^m!f+tIxsoFL_U2OljtkI{B&dURRYz-HZ0ENBO;Pt#j7rR?Khx-dCaTXr-`b
zw<k}2=iy9^=f~aeoECW4a#cD`)n@XEWPh*AZ{M9$D_U`PW4N3lm*QdX`Bg9eTtD~w
zkj$6uc8`T-boXv&SbxN;Sny0nuHhmMss5|aHCfZLA8kFbc-GW=NfNgAA2=)RicDI0
z%`YYFcIFnx`*r<u@}pL4UDRKyWqd<icgKsF6FCC6&uN%YzQf4BT;SFk`-{hg7S7gv
zG4IOiB`e$_SC=n(B-49DZH`^r^N#mUYg1~4OmoWizpZf%yZ!g1m;SQYC1Fp`33yKU
z<RA3Vcx~jBr8af<%XU9BcR7(Iw)NdihY891zEz8TF+F`s`JkWllc-4(Hwm9mc(w9)
zStVaB<BL?^{trt!+CM9&uX@k6Am6m^$ou?P?2W=xw;l|u5}1|IJL}lZq>qnvmWi+a
a&wPwI>S}<zRuwR1F?hQAxvX<aXaWEfC|=tD
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7c044394447019738fcfc8d57f017159d1fefaf8
GIT binary patch
literal 422
zc%17D@N?(olHy`uVBq!ia0vp^7C@}U!3-oXx@GJEQhotGA+A7LBAOv?ty}yWx4<Pf
zKn4(nFSc=+q#L)!4F#=*ldjRLougMc#jSA%OS#6bc8gi<9KY6mdgN&vpox+tL4Lsu
zjI$?jPCdxa`uWL?6FV1fT9u$C{im(O%PeHwrV~I#?>t=`Lp+Wjy?VQ^SwW;V(I$v5
zOK|nmzwsqTen#D5t90^z-Ot-EaMDj8CVIV(GHY~b`~5Fxb{VpG{Mr*+bx!ee5udF^
zJJ0h8k7PE@aenQ3J^cEdsp99gi(+5hdcX73-S%V86JE8QI9Yd})!kb9->bYS*}q@R
z;#6POaHi(PvKu=EmT&v-IC<Cf_>Joir`ncz>^*<n?ESml&T4lP;vaqBymX#PeTm=G
zq6Zgp4rMCGOqt^stGX$a|B~Wzz8K!=->%GYzE{MzHTha;fqeAz9%0qoS9eVpt+`pi
xce1hg#kC3f>P6g7FU3Z%&RxItm3iSM{>zCWMT-`&9R~WG!PC{xWt~$(695=xx<>#2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..436e8bb84ce8a45dbe730779ca424b02ea7b45c8
GIT binary patch
literal 410
zc%17D@N?(olHy`uVBq!ia0vp^7C@}W!3-q5u4}CVQXT<5A+A6=E-5`GAstA@CZ)N%
z`^P0^#3yF}$=JjUkW_pskPT!2$>_M$(AY%pu*BHJ)VSn~nB+8&u7q@;`Zq#XUIWb%
zFA4GsW@vEzlf(S#ds|tu#@V?CWP~`)?+0x1smwS23{>*U)5S5w<M`RDr}J7BL|Pv%
zjR|?T$o<}b-|V7u3F4b(pIY#Aw%&ivHikKVQ*ITt=sptLUZ8VU=MHB~tD+jK@{X@f
z+B)-po^=$tchH|{>1FFLm&;f4pME~0DSq|E$pvo8C#|ohxhJnX)OJ;oQC2a<$^Axx
zv#Tvf>cTT87EYb=w=_c9@{Dw<`#;$tpvdxH-xprIa^bqx`VhIFA^$Byo}9dKbzV}>
zH{D|;69YoLE*^Yfw%_FUnT>JQD*K)O2c#X$liK%Q^Sj5ok^;_v=T|=T)jYoaH{?Oe
u_mgQhOKNtu%AQ|4S2jdIEcOxCJ#nR*QKlKep?iS7X7F_Nb6Mw<&;$S(Cb8uJ
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..56ee92178b127661354e2da15809b1ee22f86976
GIT binary patch
literal 410
zc%17D@N?(olHy`uVBq!ia0vp^7C@}W!3-q5u4}CVQXT<5A+A6=J~1seDLpPJBPJm|
zJ}DzU5lE)T#--cad&Pmo)5D@-AdJ|=6d)NBp9)kQ9i0jmOp61mPtHg^d~+etEb)>c
zzhH(;<962<30tI@FRkF6cHeQo$|s4=I{)(Ne0P9KUU|AWhIkx*d-ZnTAq9bkhm7U7
zigdm_*z<q3p7v_bsn-g9_I%p^=oxn+6XU;2nTw`xx_gvY`q*nF@hV&HrSn+!@~R)z
z%TZw3JzxLNAud^^D8@Cle9!W3-!xXd6l?M6LFc>8HW_!Czv$eUz|1eE*^*Gjxh9As
zQS^|j#T*AC2@f_?&V^YAYOGHDJ^YbPth!bC^<~cxZUc9-9@byG0`nQ#PPA&rq<#uG
zHIcn}Ww>AKx^)W57kT33uUA!AE@BL<6WbX0_WI1lf2(e3XIY$Tk)1wI&T%)}=JbRm
r&UaRv6g{S+JzYoW)|0TiQm6Fu9l~u+J4!YJea+zM>gTe~DWM4fT1KOF
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a3e2d45d402ae6ceaba4a68b0f4bd3e8e9ab1db
GIT binary patch
literal 1636
zc$@)b2AlbbP)<h;3K|Lk000e1NJLTq002J#002J-1^@s6S6>j{000InNkl<ZXx{Bx
zO>f&q5S>|)iVVo8+W@`g5cJSnf%X^nclBoEW}paC<WN8b?4UgrY0_G*ZE<&JatP@T
zCbP@shw1{A0D^0ZTJgPk^Rcrsz~jCcj;}{yo!$SLfFt-L_#^lu_#^lu_#^lu_^B^c
z=R<#P(wF?#=U#jbzyMtT%fQf39u)w^Ll9Iy66oA@oCs$0=jsCj#{!V9dstr3s#tn|
zy#R0=F}7sS380(+&jq9u5l|lhc#Z&lkIR6?%$N&64iZow7&wZD(BKgOs*LQ>AO@Et
zU~aU>0qbJo-u~Q(XfJ{4v84_a?;$K4?QtS-3w(-@N)%dLXicpI7$6`ySA78BsC+s{
zNTsB2S)CF9W)n~^D0LL+tK=F2zzP6@0lo*#O5QYsfMUz@Kmnq2wPop{>)3sNWdPQ`
zOsXP8b^nmWd2YbVxdIlBU51qY0VzXtNwGD{@;Crct_IN<J3YR=z5U%e2Z$f1VCIK2
z(LYL-*H2GRzchTF40&tPECN0$eQ{!{!-2H+iM&Gy;G6^J+ylV=CBgT=yL+rc2n&m!
zgDz}RU`@-nZ41shsH$r43y7#Eq%z_FWL#r30rd<kJL6o7ai7C6gaB1lJ;=Tge63_p
z-F(3)iYQS@!rTh4RNl@|Km`EUOPQIA2oMF3uIwmN6s6o@mgRHh=};Ai0Dy=%26WEF
ztmk0kxrzv=jhrq6O{HYqA1g6_(#=x?z`8p(0C3X1SHZN`w*U|UU=ILy0N^{x-*@NG
zy%)Qq72_ykMKsnA58!d2hSvTgeGI$Fd(wg+EA-Oc?RNXsYE`Y*>$+*0qzXa^;Jpvt
z`yN~r=V)9bad4&t9^JjQR#~};IrZ7LZN2xNV~l`^(C2)4^l2`Hz}|bGS<RIIOnr8g
z&s$|~?xA#QA^^a*Z{Pm5*=)W5fTzy6s%e^!QV|M>F~WYokGtLO-ObI-JHw)ub;lZL
z5IiSJNzN=lP}JQdWa<C_Uc7km!}I6Qzkc@Y*>`7WXHQQ~PF!8ruwJhr#t1|NZQIiI
z_4OZb-n@Ca-EMzY{u>n;WUK?&AR(Lbs!W7OV?hGO^~xx1<)Q^*jGl<@R;!h3n&$Hm
z!jt`e4@89Cd%C~Br`>M%=jG+)%ZrPPAJ5Ovf4RE4dfz>8=z^U^gdXz<*?&K#!b`36
zQr(t^>#twGe$UKqv)O!hc6Rm^GoMyfRS^+1O+!Q!ce~wR+wFFHadGkMt5>hym&`l2
z(~AT7wMrB&@@NuiuOB-A*mUQWNQVvpz5#%rI?MHGtnL235!d&L^yV|}AyeHaDj2h4
z!-dRR%?KR~N#tEJ4mL{HA#0C$#!=C}H1Kf(3<~yTZSFcN2Vr5$dMR>pfeuPGL&jL1
zMQyFSD<BphQ8g;4lcy7Lqo3za$UG=Aytp2W;HE1%&J;U~@7ScQjsjp$P!zzW<^2T9
z(}2Yn*|A}xW1`1kEIU!DfED;C=J5iowq}X^A=B$0lzu%i*pbm#kX;v6?of#$sL$OF
z0RHYGJc;NnWzUA}V_~3NWUnq)%ChRJNFt(0lTkGq*a&#3bTy^ITMT?^^Uw-_S`pT=
z5;EX}=;Oys1}Ulc7f*FM|2o6kEc}^Nx!hZ^60+Yb@ckUEAR#C9R2{IG<%d>NoM|&V
z;R2FK5|(ErdoHl_EXhyl-g2UEZuz}bOH8z$yZ}!Me2RozfLpY6olAA3tZJ2{dXJ7&
z6Y{Q;Js99MDXgM>%I}LB8DpY-YI8ulg5FaDmD@p);tdN&1!~M1{vbY|3W9p9T|mbI
zT|D^#c|(EJVT$BSIytF;hsF*H8m{4tJwDm=xEI9?B}!si$m+%W^C<#Kf9pnx{s0$P
zo=3`^rvRTC?Mn&Zf?buUY6CLdhi3wxys*h_1g3r}f-2W*z|s}=o%MB=Kt&3}@DG)E
zL~DDN<t+koJ-$?cj4D;?4i6OgcyVf5D%zL+4o(L|Yw<&w9sv}{d=l`f;4K1D(nn)?
zEB(Jsq%fU9hNAkUpQKOOu^CbTCZuTv%xK6vXVtOA%%T-uN%fcWbt_Ix*@v4A{6U6%
i;QwaEiGFq9XTlG09-7iK-TF2F0000<MNUMnLSTYwT@arD
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0d76eda5a494b031bba8df437ffe117f5e8cacdd
GIT binary patch
literal 951
zc%17D@N?(olHy`uVBq!ia0vp^VIa)G3?!2}CLRS+Q2{<7u0Yz;H#|N$JuD_ME-4*|
zViMD05>n#9YzT@^%7C&Hp=4}g8jukcmmHk{Vn|6z0ZjlhA`?@=Dl_7fGgg1JO97fL
zTN30K%)tIr@YYg0gU?UOvtMsW2%X;RaZy_B1ml673e64gr$0N!z`*q0)5S3);_#a5
z-s_qT1Y9l}9TWJcQnPr;_x)$ZCGDphZB%-(xj|}6iK_H|qrX+R(=|5lu6c55cbaIv
z(FGGP1E)a6Kt|iSF0xm2l(xKx3;VmvOnBiLr{mH4_AETK<&o|DM_bvXq%3XZiZT`?
zoOVt)pc1$5;Vwr$+Y6@?50tL1n9As*dT-VB+}{3KtglO|H~-jRF1F^<TiJVaS+h%K
zpZ)NxhgG}ewSa}#nhUv|3sMep-Bn705klNyFQ!FoaN1t7G9lXJ@x4vaNeAEBnYG#)
zp2+KDRX=vQ`OUP5f+QPT5O?`;?{Lp9=8X?pPEB=~=o!VCbkH-h(NpNtJ5z;453fF*
zWT0pmA*C|I$guC*_k;J3NAI}u;AUaF>g2<@2VGCstKVe$BJ@OT`i+>Tf7-LI#{7>n
zIDOXhLas&bYn6h;qi=+Ixcpz=RoJkxQ%e2nMwaf3bro-et2kFlF8H)wcyn%^087&b
zi6v57*7l^iMgRYQ|IO)u?G;5icltSto2$RCd0;2~(Cg|3|Cfr6bN)1on>~H<+F(`m
z{I$EvqW^7u*?2URr$%*2(!pefrO5}A83ZFPNI1xd*|1IMW9?@=0u*QP)LB+^RkOeG
zNYnn(GhA$zI(aM{2Nq}u&tI)1B5Ta^aN|~=n-&(@!tNF~ULR_l&$+kZbgy1aHIw~A
z-r#KyZ|+p?>yqNUa&qY(u^;Ta^KKk!Vpb9VXH@34^F-$H_=~$@RL(S)9-m)Pa_h6l
zjC=L5bIv&}+$+Voz27WshqRK&4iAHU_sb%ew=v0c=8L&LY!Tw}QQ2{v?I5SBNC)S9
z_COK82H9EeTjYiOJ}gOAye0pZ@8B}O1mPT&iY@ki$6lK!Kilj#_rr#s?v^co|He<g
zrdW7r@xeRaXFfS*`!AW#&w*b_sAX2lt%odEHcn*FZI5gh4md3ra^WA>DQm;Y=~7$d
zgXVm=a%0io39pWva8SFw-oi2Q+oa6;gPh?#QqkXQ=WdBFeU!dcH1IR4L-FNMH;J9u
P44^FN>gTe~DWM4f!KAHa
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1b013db090f52a570453324bc6c121cb92081d5c
GIT binary patch
literal 509
zc%17D@N?(olHy`uVBq!ia0vp^p+M}w!3-p03{E=$DenNE5LX}_x7ICgjT?}RUF~Yq
zs|;iV86Xi514Krza`j%U2Ug}9y~-tQxdU7aL@<7>Tl`x0ukDt+K;tAzg8YIR-v9pQ
z!rW(B(&#r+k8N>;yx`SSJJzN>DOEqv#=yYn>FMGa5^?zLwcA-O1|qEwWrh0}C-e24
z{B57<EtwXWb4WtG)#-osXT_WwodrdUuWRx6&G6N#6|Gj<W%hEWS;&{8-&{mi&g*QN
zJ5wXV+ht<D{vzQEEoDkwW=dTmg08%Rt`j>JSqr+>>Sl4hSSxk4vLyN+*RLP$KQn%N
zuP{Bf>7wS6^M^v*elu0sFN&?Yw$<m$)n^eeW(a*UYhM-oV#2<qJj{3b#gD{wPcwhI
zc5lb7$IoX(N*>jpqTc%FkC~Reh|sCCId<J%-yDpuEMCN|J}oMw=9GAhs#dI)$aAZZ
zHF_JXG#1Q#ay!`7VRMeN%fvtzkw|}+iMKm6vaHp*!hBr<<?p+?TTbM45z*yW>vGfU
znkg7);;z&cqp&DrXZl4)Jrffb5lL5<i*HtHOp_2jD)e~Kj*1-`X>H5ZwRH?lIX9=?
gUG#14)XlXFJ#Tk!+^D|X2^jDUp00i_>zopr0Q_^$egFUf
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..51e954466cd9c67185409693a25d802fd108ae17
GIT binary patch
literal 451
zc%17D@N?(olHy`uVBq!ia0vp^VL<G{!3-pIrWu6-DVqSF5LX}_my`}fZvGJvMoe5v
zTv7%YrN<?v$AbmI?6laV^w`AIh^U09_@wyc4C_~G0)hI4OM?7@8D8yXzul&MVqsO}
z_w|$Aa*Tx}KJ9b*#=yX+?CIhd5^?zL_0w5L40u`}DxdtQyK!>Qz5lV+&6;l=oVRXR
zxbWY#NAV}w7$+QAGCizlPsqXLyEZHi)fZGhc=_R%@+)Z*bq~K|II_~Sf+6C{5za|c
z7aLc(ajpsTJF}T5XmiFq)fWPbH}6nbzP@w+wHe=E`5$C0-u7{$>vUn?`8pGB^(`zG
zi>?;jySenM!~|R8s`T0R)33^(ee?hHz3lw-vVWY$*8f|6r@h|_WW?V*(|0OPS?}k^
zx>+J>Tzig2occ6n+Vr4D$Ii{ljj_6T<-*q~apg9iZ|WB2Gr5>DtrYbRQ)XHn$(eQJ
zMrngA$E{d~2hQJD9Y4KqVwv-8KIV^=m(AzwUBLcdSBCG)WY5_eFHY_bb4~9LH|Jn0
k3Ge^+N6OI2F5xTV*J=COWSQQv07H|()78&qol`;+0L^yAh5!Hn
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ef6999aea1388fd2195132d812ce7f8c99dc20d9
GIT binary patch
literal 460
zc$@*u0W<!IP)<h;3K|Lk000e1NJLTq00341002Y?0{{R3ES54=0000vP)t-s0001A
zVrNoRRb60aUSnroVrO1sXJ2AxUSelkTU%aYW?y1wU1DZjTxDNlW?f=uP*6}_VrO4t
zXtJoJh5!Hn5_D2dQvkmRHT<pK^yd$D(rg}yMFg6uPGdd*00A&bL_t(o!|mA1ZiFxl
zKv4(h{Q$=Q|FIpZ&=e#>Nt{JTxl0yD-e4txJye!hvI^lRFNrPbcNMeB;qLP)VohzV
z=4JK95_A8Uh2{o3*uf5Vu%E<!H&|Xa>Wkx-5wU@_be*baXDSq{*+~>wQOy=zQ_Ys$
zYBsy9Y<5}M?6R`iWoEO>&w?{OSl4CR6sQB6G<9InQU?|dbzs*(J=lfHY~^*&_hBO)
zGdwKPA>l+btKr>?eia-{qLo$WA6KWmcdA*5aCB|T`>dLk{<LTe<~^C&`Ufkg=v~-8
zWEWb)=$94X=t^KLyBheLW*ebfNO$>)kr#b~%J^{1Kz~RhJZ$ym4wbWm=Otx|s7g?m
z^ZE+4bApDJS(q~^!;H|}ivFcUu+mr^)GIC%l=}jScB8_c?Mq4k0000<MNUMnLSTZa
Cy2zaX
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/overlay_share_dialog.xml
@@ -0,0 +1,117 @@
+<?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/. -->
+
+<!-- Serves to position the content on the screen (bottom, centered) and provide the drop-shadow -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sharedialog"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginLeft="15dp"
+    android:layout_marginRight="15dp"
+    android:layout_marginBottom="-12dp"
+    android:paddingTop="30dp"
+    android:layout_gravity="bottom|center"
+    android:clipChildren="false"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/share_overlay_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:orientation="vertical"
+        android:background="@drawable/share_overlay_background">
+
+        <!-- Header -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="0dp"
+            android:background="@color/background_light"
+            android:orientation="vertical"
+            android:paddingTop="10dp"
+            android:paddingBottom="15dp"
+            android:paddingLeft="15dp"
+            android:paddingRight="15dp"
+            android:layout_gravity="center">
+
+            <!-- Title -->
+            <TextView
+                android:id="@id/title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingBottom="7dp"
+                android:ellipsize="end"
+                android:maxLines="2"
+                android:scrollHorizontally="true"
+                android:textColor="@color/text_color_primary"
+                android:textSize="17sp"/>
+
+            <!-- Subtitle (url) -->
+            <TextView
+                android:id="@+id/subtitle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textColor="@color/text_color_secondary"/>
+
+        </LinearLayout>
+
+        <!-- Buttons -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@color/overlay_share_background_colour"
+            android:orientation="vertical">
+
+            <!-- TODO: Once API 11 is available, stick "showDividers=middle" into the parent and get rid
+                       of these evil separator views. -->
+
+            <!-- "Send to Firefox Sync" -->
+            <org.mozilla.gecko.overlays.ui.SendTabList
+                style="@style/ShareOverlayButton"
+                android:id="@+id/overlay_send_tab_btn"
+                android:background="@color/overlay_share_background_colour"
+                android:padding="0dp"/>
+
+            <!-- Evil separator -->
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:background="@color/background_light"/>
+
+            <!-- "Add to reading list" -->
+            <TextView
+                style="@style/ShareOverlayButton.Text"
+                android:id="@+id/overlay_share_reading_list_btn"
+                android:text="@string/overlay_share_reading_list_btn_label"
+                android:drawableLeft="@drawable/overlay_readinglist_icon"/>
+
+            <!-- Evil separator -->
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:background="@color/background_light"/>
+
+            <!-- "Add bookmark" -->
+            <TextView
+                style="@style/ShareOverlayButton.Text"
+                android:id="@+id/overlay_share_bookmark_btn"
+                android:text="@string/overlay_share_bookmark_btn_label"
+                android:drawableLeft="@drawable/overlay_bookmark_icon"/>
+
+        </LinearLayout>
+    </LinearLayout>
+
+    <!-- Firefox logo (has to appear higher in the z-order than the content. -->
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/share_overlay_content"
+        android:scaleType="center"
+        android:layout_centerHorizontal="true"
+        android:src="@drawable/icon"
+        android:layout_marginBottom="-6dp"/>
+</RelativeLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/overlay_share_send_tab_button.xml
@@ -0,0 +1,18 @@
+<?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/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <ListView
+        style="@style/ShareOverlayButton"
+        android:id="@+id/device_list"
+        android:padding="0dp" >
+    </ListView>
+
+</LinearLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/overlay_share_send_tab_item.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/ShareOverlayButton.Text"/>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/overlay_share_toast.xml
@@ -0,0 +1,58 @@
+<?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/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/overlay_share_toast"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@drawable/share_overlay_background"
+    android:layout_marginLeft="5dp"
+    android:layout_marginRight="5dp"
+    android:layout_gravity="bottom|center">
+
+    <!-- Header -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="0dp"
+        android:background="@color/background_light"
+        android:orientation="horizontal"
+        android:paddingLeft="5dp"
+        android:paddingRight="10dp"
+        android:paddingTop="5dp"
+        android:paddingBottom="5dp">
+
+        <!-- Large attractive green tick with label to the right -->
+        <TextView
+            style="@style/ShareOverlayButton.Text"
+            android:id="@+id/overlay_toast_message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            android:textColor="@color/text_color_primary"
+            android:textSize="14sp"
+            android:drawableLeft="@drawable/overlay_check"/>
+
+        <!-- Evil separator -->
+        <View
+            android:id="@+id/overlay_toast_separator"
+            android:layout_marginTop="15dp"
+            android:layout_marginBottom="15dp"
+            android:layout_height="match_parent"
+            android:layout_width="1dp"
+            android:background="@color/background_light"/>
+
+        <!-- Retry button -->
+        <Button
+            android:id="@+id/overlay_toast_retry_btn"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/overlay_share_retry"
+            android:onClick="selfDestruct" />
+
+    </LinearLayout>
+</LinearLayout>
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -43,16 +43,22 @@
   <color name="text_color_secondary">#777777</color>
   <color name="text_color_tertiary">#9198A1</color>
 
   <!-- Default inverse colors -->
   <color name="text_color_primary_inverse">#FFFFFF</color>
   <color name="text_color_secondary_inverse">#DDDDDD</color>
   <color name="text_color_tertiary_inverse">#A4A7A9</color>
 
+  <!-- Colour used for share overlay button labels -->
+  <color name="text_color_overlaybtn">#666666</color>
+
+  <!-- Colour used for share overlay button background -->
+  <color name="overlay_share_background_colour">#FFD0CECB</color>
+
   <!-- Disabled colors -->
   <color name="text_color_primary_disable_only">#999999</color>
 
   <!-- Hint colors -->
   <color name="text_color_hint">#666666</color>
   <color name="text_color_hint_inverse">#7F828A</color>
   <color name="text_color_hint_floating_focused">#33b5e5</color>
 
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -758,16 +758,39 @@
     </style>
 
     <style name="GeckoActionBar.Buttons">
         <item name="android:background">@android:color/transparent</item>
         <item name="android:textColor">@color/text_color_primary</item>
         <item name="android:gravity">right</item>
     </style>
 
+    <style name="ShareOverlayButton">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:minHeight">60dp</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:paddingLeft">15dp</item>
+        <item name="android:paddingRight">15dp</item>
+        <item name="android:paddingTop">17dp</item>
+        <item name="android:paddingBottom">17dp</item>
+        <item name="android:focusableInTouchMode">false</item>
+        <item name="android:clickable">true</item>
+        <item name="android:background">@android:drawable/list_selector_background</item>
+        <item name="android:layout_margin">0dp</item>
+    </style>
+
+    <style name="ShareOverlayButton.Text">
+        <item name="android:drawablePadding">15dp</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">@color/text_color_overlaybtn</item>
+        <item name="android:ellipsize">marquee</item>
+    </style>
+
     <style name="TabInput"></style>
 
     <style name="TabInput.TabWidget">
         <item name="android:divider">@drawable/divider_vertical</item>
         <item name="android:background">@drawable/tab_indicator_background</item>
     </style>
 
     <style name="TabInput.Tab">
@@ -775,16 +798,23 @@
         <item name="android:gravity">center</item>
         <item name="android:minHeight">@dimen/menu_item_row_height</item>
     </style>
 
     <style name="FloatingHintEditText" parent="android:style/Widget.EditText">
         <item name="android:paddingTop">0dp</item>
     </style>
 
+    <!-- Make the share overlay activity appear like an overlay. -->
+    <style name="ShareOverlayActivity">
+        <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>
+    </style>
     <style name="OnboardStartLayout">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
     </style>
 
     <style name="OnboardStartTextAppearance">
         <item name="android:textColor">#5F636B</item>
     </style>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -105,16 +105,25 @@
   <string name="find_next">&find_next;</string>
   <string name="find_close">&find_close;</string>
 
   <string name="media_casting_to">&media_casting_to;</string>
   <string name="media_play">&media_play;</string>
   <string name="media_pause">&media_pause;</string>
   <string name="media_stop">&media_stop;</string>
 
+  <string name="overlay_share_send_other">&overlay_share_send_other;</string>
+  <string name="overlay_share_header">&overlay_share_header;</string>
+  <string name="overlay_share_bookmark_btn_label">&overlay_share_bookmark_btn_label;</string>
+  <string name="overlay_share_reading_list_btn_label">&overlay_share_reading_list_btn_label;</string>
+  <string name="overlay_share_send_tab_btn_label">&overlay_share_send_tab_btn_label;</string>
+  <string name="overlay_share_no_url">&overlay_share_no_url;</string>
+  <string name="overlay_share_retry">&overlay_share_retry;</string>
+  <string name="overlay_share_select_device">&overlay_share_select_device;</string>
+
   <string name="settings">&settings;</string>
   <string name="settings_title">&settings_title;</string>
   <string name="pref_category_advanced">&pref_category_advanced;</string>
   <string name="pref_category_customize">&pref_category_customize;</string>
   <string name="pref_category_search">&pref_category_search3;</string>
   <string name="pref_category_search_summary">&pref_category_search_summary;</string>
   <string name="pref_category_display">&pref_category_display;</string>
   <string name="pref_category_privacy_short">&pref_category_privacy_short;</string>