diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index 2444b62c7c..a83d849113 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              xmlns:app="http://schemas.android.com/apk/res-auto"
-              android:layout_width="fill_parent"
-              android:layout_height="fill_parent"
-              xmlns:tools="http://schemas.android.com/tools"
-              android:background="?conversation_background"
-              android:orientation="vertical">
-
-    <RelativeLayout
-            android:id="@+id/layout_container"
-            android:layout_width="fill_parent"
-            android:layout_height="fill_parent"
-            android:layout_weight="1"
-            android:orientation="vertical"
-            android:gravity="bottom">
+<org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:id="@+id/layout_container"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:background="?conversation_background"
+        android:orientation="vertical">
+
+    <RelativeLayout android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    android:layout_weight="1"
+                    android:orientation="vertical"
+                    android:gravity="bottom">
 
         <FrameLayout android:id="@+id/fragment_content"
                      android:layout_width="match_parent"
@@ -141,11 +141,4 @@
 
         </LinearLayout>
     </RelativeLayout>
-
-    <ViewStub android:id="@+id/emoji_drawer_stub"
-              android:inflatedId="@+id/emoji_drawer"
-              android:layout="@layout/emoji_drawer_stub"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content" />
-
-</LinearLayout>
+</org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout>
diff --git a/res/layout/emoji_drawer.xml b/res/layout/emoji_drawer.xml
index 65e96350f2..fa4849f634 100644
--- a/res/layout/emoji_drawer.xml
+++ b/res/layout/emoji_drawer.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                                                                 xmlns:app="http://schemas.android.com/apk/res-auto"
-                                                                 android:id="@+id/container"
-                                                                 android:orientation="vertical"
-                                                                 android:layout_width="match_parent"
-                                                                 android:layout_height="match_parent">
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              android:id="@+id/container"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
 
     <LinearLayout android:background="?emoji_tab_strip_background"
                   android:orientation="horizontal"
@@ -47,4 +47,4 @@
             android:layout_height="match_parent"
             android:background="?emoji_background" />
 
-</org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d4b87973bd..56b0af4c42 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <dimen name="emoji_drawer_size">32sp</dimen>
+    <dimen name="min_keyboard_size">50dp</dimen>
     <dimen name="min_emoji_drawer_height">200dp</dimen>
     <dimen name="emoji_drawer_item_padding">5dp</dimen>
     <dimen name="emoji_drawer_indicator_height">1.5dp</dimen>
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index f76a2f57dc..63df091c51 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -23,7 +23,6 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
@@ -35,11 +34,11 @@ import android.os.Build;
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.support.annotation.NonNull;
-import android.support.v4.graphics.drawable.DrawableCompat;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.TextWatcher;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -48,7 +47,6 @@ import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
 import android.view.View.OnKeyListener;
-import android.view.ViewStub;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
@@ -63,8 +61,11 @@ import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
 import org.thoughtcrime.securesms.color.MaterialColor;
 import org.thoughtcrime.securesms.components.AnimatingToggle;
 import org.thoughtcrime.securesms.components.ComposeText;
+import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
+import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
 import org.thoughtcrime.securesms.components.SendButton;
-import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
+import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener;
+import org.thoughtcrime.securesms.components.emoji.EmojiPopup;
 import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
 import org.thoughtcrime.securesms.contacts.ContactAccessor;
 import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
@@ -131,7 +132,8 @@ import static org.whispersystems.textsecure.internal.push.TextSecureProtos.Group
 public class ConversationActivity extends PassphraseRequiredActionBarActivity
     implements ConversationFragment.ConversationFragmentListener,
                AttachmentManager.AttachmentListener,
-               RecipientsModifiedListener
+               RecipientsModifiedListener,
+               OnKeyboardShownListener
 {
   private static final String TAG = ConversationActivity.class.getSimpleName();
 
@@ -150,23 +152,24 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
   private static final int GROUP_EDIT        = 5;
   private static final int CAPTURE_PHOTO     = 6;
 
-  private   MasterSecret          masterSecret;
-  protected ComposeText           composeText;
-  private   AnimatingToggle       buttonToggle;
-  private   SendButton            sendButton;
-  private   ImageButton           attachButton;
-  protected ConversationTitleView titleView;
-  private   TextView              charactersLeft;
-  private   ConversationFragment  fragment;
-  private   Button                unblockButton;
-  private   View                  composePanel;
-  private   View                  composeBubble;
+  private   MasterSecret              masterSecret;
+  protected ComposeText               composeText;
+  private   AnimatingToggle           buttonToggle;
+  private   SendButton                sendButton;
+  private   ImageButton               attachButton;
+  protected ConversationTitleView     titleView;
+  private   TextView                  charactersLeft;
+  private   ConversationFragment      fragment;
+  private   Button                    unblockButton;
+  private   KeyboardAwareLinearLayout container;
+  private   View                      composePanel;
+  private   View                      composeBubble;
 
   private AttachmentTypeSelectorAdapter attachmentAdapter;
   private AttachmentManager             attachmentManager;
   private BroadcastReceiver             securityUpdateReceiver;
   private BroadcastReceiver             groupUpdateReceiver;
-  private Optional<EmojiDrawer>         emojiDrawer = Optional.absent();
+  private Optional<EmojiPopup>          emojiPopup = Optional.absent();
   private EmojiToggle                   emojiToggle;
 
   private Recipients recipients;
@@ -360,13 +363,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
   @Override
   public void onBackPressed() {
     if (isEmojiDrawerOpen()) {
-      getEmojiDrawer().hide();
-      emojiToggle.toggle();
+      hideEmojiPopup(false);
     } else {
       super.onBackPressed();
     }
   }
 
+  @Override
+  public void onKeyboardShown() {
+    hideEmojiPopup(true);
+  }
+
   //////// Event Handlers
 
   private void handleReturnToConversationList() {
@@ -730,16 +737,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
   }
 
   private void initializeViews() {
-    buttonToggle   = (AnimatingToggle)findViewById(R.id.button_toggle);
-    sendButton     = (SendButton)  findViewById(R.id.send_button);
-    attachButton   = (ImageButton) findViewById(R.id.attach_button);
-    composeText    = (ComposeText) findViewById(R.id.embedded_text_editor);
-    charactersLeft = (TextView)    findViewById(R.id.space_left);
-    emojiToggle    = (EmojiToggle) findViewById(R.id.emoji_toggle);
-    titleView      = (ConversationTitleView) getSupportActionBar().getCustomView();
-    unblockButton  = (Button)      findViewById(R.id.unblock_button);
-    composePanel   =               findViewById(R.id.bottom_panel);
-    composeBubble  =               findViewById(R.id.compose_bubble);
+    titleView      = (ConversationTitleView)     getSupportActionBar().getCustomView();
+    buttonToggle   = (AnimatingToggle)           findViewById(R.id.button_toggle);
+    sendButton     = (SendButton)                findViewById(R.id.send_button);
+    attachButton   = (ImageButton)               findViewById(R.id.attach_button);
+    composeText    = (ComposeText)               findViewById(R.id.embedded_text_editor);
+    charactersLeft = (TextView)                  findViewById(R.id.space_left);
+    emojiToggle    = (EmojiToggle)               findViewById(R.id.emoji_toggle);
+    unblockButton  = (Button)                    findViewById(R.id.unblock_button);
+    composePanel   =                             findViewById(R.id.bottom_panel);
+    composeBubble  =                             findViewById(R.id.compose_bubble);
+    container      = (KeyboardAwareLinearLayout) findViewById(R.id.layout_container);
+
+    container.addOnKeyboardShownListener(this);
 
     int[]      attributes   = new int[]{R.attr.conversation_item_bubble_background};
     TypedArray colors       = obtainStyledAttributes(attributes);
@@ -797,16 +807,44 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
     getSupportActionBar().setDisplayShowTitleEnabled(false);
   }
 
-  private EmojiDrawer getEmojiDrawer() {
-    if (emojiDrawer.isPresent()) return emojiDrawer.get();
-    EmojiDrawer emojiDrawer = (EmojiDrawer)((ViewStub)findViewById(R.id.emoji_drawer_stub)).inflate();
-    emojiDrawer.setComposeEditText(composeText);
-    this.emojiDrawer = Optional.of(emojiDrawer);
-    return emojiDrawer;
+  private EmojiPopup getEmojiPopup() {
+    if (!emojiPopup.isPresent()) {
+      EmojiPopup emojiPopup = new EmojiPopup(getWindow().getDecorView());
+      emojiPopup.setEmojiEventListener(new EmojiEventListener() {
+        @Override public void onKeyEvent(KeyEvent keyEvent) {
+          composeText.dispatchKeyEvent(keyEvent);
+        }
+
+        @Override public void onEmojiSelected(String emoji) {
+          composeText.insertEmoji(emoji);
+        }
+      });
+      this.emojiPopup = Optional.of(emojiPopup);
+    }
+    return emojiPopup.get();
+  }
+
+  private void showEmojiPopup() {
+    Log.w(TAG, "showEmojiPopup()");
+    int height = Math.max(getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height),
+                          container.getKeyboardHeight());
+    container.padForCustomKeyboard(height);
+    getEmojiPopup().show(height);
+    emojiToggle.setToIme();
+  }
+
+  private void hideEmojiPopup(boolean expectingKeyboard) {
+    if (isEmojiDrawerOpen()) {
+      getEmojiPopup().dismiss();
+      if (!expectingKeyboard) {
+        container.unpadForCustomKeyboard();
+      }
+    }
+    emojiToggle.setToEmoji();
   }
 
   private boolean isEmojiDrawerOpen() {
-    return emojiDrawer.isPresent() && emojiDrawer.get().isOpen();
+    return emojiPopup.isPresent() && emojiPopup.get().isShowing();
   }
 
   private void initializeResources() {
@@ -1034,10 +1072,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
   }
 
   private void calculateCharactersRemaining() {
-    int            charactersSpent = composeText.getText().toString().length();
+    int             charactersSpent = composeText.getText().toString().length();
     TransportOption transportOption = sendButton.getSelectedTransport();
-    
-    CharacterState characterState = transportOption.calculateCharacters(charactersSpent);
+    CharacterState  characterState  = transportOption.calculateCharacters(charactersSpent);
 
     if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) {
       charactersLeft.setText(characterState.charactersRemaining + "/" + characterState.maxMessageSize
@@ -1235,12 +1272,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
       InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
 
       if (isEmojiDrawerOpen()) {
+        hideEmojiPopup(true);
         input.showSoftInput(composeText, 0);
-        getEmojiDrawer().hide();
       } else {
+        container.postOnKeyboardClose(new Runnable() {
+          @Override public void run() {
+            showEmojiPopup();
+          }
+        });
         input.hideSoftInputFromWindow(composeText.getWindowToken(), 0);
-
-        getEmojiDrawer().show();
       }
     }
   }
@@ -1288,9 +1328,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
 
     @Override
     public void onClick(View v) {
-      if (isEmojiDrawerOpen()) {
-        emojiToggle.performClick();
-      }
+      hideEmojiPopup(true);
     }
 
     @Override
@@ -1317,8 +1355,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
 
     @Override
     public void onFocusChange(View v, boolean hasFocus) {
-      if (hasFocus && isEmojiDrawerOpen()) {
-        emojiToggle.performClick();
+      if (hasFocus) {
+        hideEmojiPopup(true);
       }
     }
   }
diff --git a/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java b/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
index f6b2134fdc..e16625c79f 100644
--- a/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
+++ b/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
@@ -16,68 +16,84 @@
  */
 package org.thoughtcrime.securesms.components;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.preference.PreferenceManager;
+import android.support.v7.widget.LinearLayoutCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
-import android.widget.LinearLayout;
 
 import org.thoughtcrime.securesms.R;
 
 import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * LinearLayout that, when a view container, will report back when it thinks a soft keyboard
  * has been opened and what its height would be.
  */
-public class KeyboardAwareLinearLayout extends LinearLayout {
-  private static final String TAG  = KeyboardAwareLinearLayout.class.getSimpleName();
-  private static final Rect   rect = new Rect();
+public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
+  private static final String TAG = KeyboardAwareLinearLayout.class.getSimpleName();
+
+  private final Rect                          oldRect         = new Rect();
+  private final Rect                          newRect         = new Rect();
+  private final Set<OnKeyboardHiddenListener> hiddenListeners = new HashSet<>();
+  private final Set<OnKeyboardShownListener>  shownListeners  = new HashSet<>();
+  private final int                           minKeyboardSize;
+
+  private boolean keyboardOpen;
 
   public KeyboardAwareLinearLayout(Context context) {
-    super(context);
+    this(context, null);
   }
 
   public KeyboardAwareLinearLayout(Context context, AttributeSet attrs) {
-    super(context, attrs);
+    this(context, attrs, 0);
   }
 
-  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
   public KeyboardAwareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
     super(context, attrs, defStyle);
+    minKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_keyboard_size);
   }
 
-  /**
-   * inspired by http://stackoverflow.com/a/7104303
-   * @param widthMeasureSpec width measure
-   * @param heightMeasureSpec height measure
-   */
-  @Override
-  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-    int res = getResources().getIdentifier("status_bar_height", "dimen", "android");
+  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    Log.w(TAG, String.format("onMeasure(%s, %s)", MeasureSpec.toString(widthMeasureSpec), MeasureSpec.toString(heightMeasureSpec)));
+    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+    int res             = getResources().getIdentifier("status_bar_height", "dimen", "android");
     int statusBarHeight = res > 0 ? getResources().getDimensionPixelSize(res) : 0;
 
     final int availableHeight = this.getRootView().getHeight() - statusBarHeight - getViewInset();
-    getWindowVisibleDisplayFrame(rect);
+    getWindowVisibleDisplayFrame(newRect);
 
-    final int keyboardHeight = availableHeight - (rect.bottom - rect.top);
+    final int oldKeyboardHeight = availableHeight - (oldRect.bottom - oldRect.top);
+    final int keyboardHeight    = availableHeight - (newRect.bottom - newRect.top);
 
-    if (keyboardHeight > getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height)) {
-      onKeyboardShown(keyboardHeight);
+    if (keyboardHeight - oldKeyboardHeight > minKeyboardSize && !keyboardOpen) {
+      onKeyboardOpen(keyboardHeight);
+    } else if (oldKeyboardHeight - keyboardHeight > minKeyboardSize && keyboardOpen) {
+      onKeyboardClose();
     }
 
-    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    oldRect.set(newRect);
+  }
+
+  public void padForCustomKeyboard(int height) {
+    setPadding(0, 0, 0, height);
+  }
+
+  public void unpadForCustomKeyboard() {
+    setPadding(0, 0, 0, 0);
   }
 
-  public int getViewInset() {
+  private int getViewInset() {
     if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
       return 0;
     }
@@ -100,8 +116,9 @@ public class KeyboardAwareLinearLayout extends LinearLayout {
     return 0;
   }
 
-  protected void onKeyboardShown(int keyboardHeight) {
-    Log.w(TAG, "keyboard shown, height " + keyboardHeight);
+  protected void onKeyboardOpen(int keyboardHeight) {
+    keyboardOpen = true;
+    Log.w(TAG, "onKeyboardOpen(" + keyboardHeight + ")");
 
     WindowManager wm = (WindowManager) getContext().getSystemService(Activity.WINDOW_SERVICE);
     if (wm == null || wm.getDefaultDisplay() == null) {
@@ -118,10 +135,22 @@ public class KeyboardAwareLinearLayout extends LinearLayout {
       case Surface.ROTATION_180:
         setKeyboardPortraitHeight(keyboardHeight);
     }
+    notifyShownListeners();
+    unpadForCustomKeyboard();
+  }
+
+  protected void onKeyboardClose() {
+    keyboardOpen = false;
+    Log.w(TAG, "onKeyboardClose()");
+    notifyHiddenListeners();
+  }
+
+  public boolean isKeyboardOpen() {
+    return keyboardOpen;
   }
 
   public int getKeyboardHeight() {
-    WindowManager      wm    = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
+    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
     if (wm == null || wm.getDefaultDisplay() == null) {
       throw new AssertionError("WindowManager was null or there is no default display");
     }
@@ -161,4 +190,52 @@ public class KeyboardAwareLinearLayout extends LinearLayout {
                      .edit().putInt("keyboard_height_portrait", height).apply();
   }
 
+  public void postOnKeyboardClose(final Runnable runnable) {
+    if (keyboardOpen) {
+      addOnKeyboardHiddenListener(new OnKeyboardHiddenListener() {
+        @Override public void onKeyboardHidden() {
+          removeOnKeyboardHiddenListener(this);
+          runnable.run();
+        }
+      });
+    } else {
+      runnable.run();
+    }
+  }
+
+  public void addOnKeyboardHiddenListener(OnKeyboardHiddenListener listener) {
+    hiddenListeners.add(listener);
+  }
+
+  public void removeOnKeyboardHiddenListener(OnKeyboardHiddenListener listener) {
+    hiddenListeners.remove(listener);
+  }
+
+  public void addOnKeyboardShownListener(OnKeyboardShownListener listener) {
+    shownListeners.add(listener);
+  }
+
+  public void removeOnKeyboardShownListener(OnKeyboardShownListener listener) {
+    shownListeners.remove(listener);
+  }
+
+  private void notifyHiddenListeners() {
+    for (OnKeyboardHiddenListener listener : hiddenListeners) {
+      listener.onKeyboardHidden();
+    }
+  }
+
+  private void notifyShownListeners() {
+    for (OnKeyboardShownListener listener : shownListeners) {
+      listener.onKeyboardShown();
+    }
+  }
+
+  public interface OnKeyboardHiddenListener {
+    void onKeyboardHidden();
+  }
+
+  public interface OnKeyboardShownListener {
+    void onKeyboardShown();
+  }
 }
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
index 4a39605183..26695412d4 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
@@ -5,6 +5,7 @@ import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
+import android.support.v7.widget.LinearLayoutCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -14,12 +15,10 @@ import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
 
 import com.astuetz.PagerSlidingTabStrip;
 
 import org.thoughtcrime.securesms.R;
-import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
 import org.thoughtcrime.securesms.components.RepeatableImageKey;
 import org.thoughtcrime.securesms.components.RepeatableImageKey.KeyEventListener;
 import org.thoughtcrime.securesms.components.emoji.EmojiPageView.EmojiSelectionListener;
@@ -28,70 +27,50 @@ import org.thoughtcrime.securesms.util.ResUtil;
 import java.util.LinkedList;
 import java.util.List;
 
-public class EmojiDrawer extends KeyboardAwareLinearLayout {
+public class EmojiDrawer extends LinearLayoutCompat {
   private static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
 
-  private EmojiEditText             composeText;
-  private KeyboardAwareLinearLayout container;
-  private ViewPager                 pager;
-  private List<EmojiPageModel>      models;
-  private PagerSlidingTabStrip      strip;
-  private RecentEmojiPageModel      recentModel;
+  private LinearLayout         container;
+  private ViewPager            pager;
+  private List<EmojiPageModel> models;
+  private PagerSlidingTabStrip strip;
+  private RecentEmojiPageModel recentModel;
+  private EmojiEventListener   listener;
 
   public EmojiDrawer(Context context) {
-    super(context);
-    init();
+    this(context, null);
   }
 
   public EmojiDrawer(Context context, AttributeSet attrs) {
-    super(context, attrs);
-    init();
+    this(context, attrs, 0);
   }
 
   public EmojiDrawer(Context context, AttributeSet attrs, int defStyle) {
     super(context, attrs, defStyle);
-    init();
-  }
-
-  public void setComposeEditText(EmojiEditText composeText) {
-    this.composeText = composeText;
-  }
-
-  private void init() {
     final View v = LayoutInflater.from(getContext()).inflate(R.layout.emoji_drawer, this, true);
     initializeResources(v);
     initializePageModels();
     initializeEmojiGrid();
   }
 
+  public void setEmojiEventListener(EmojiEventListener listener) {
+    this.listener = listener;
+  }
+
   private void initializeResources(View v) {
     Log.w("EmojiDrawer", "initializeResources()");
-    this.container = (KeyboardAwareLinearLayout) v.findViewById(R.id.container);
-    this.pager     = (ViewPager)                 v.findViewById(R.id.emoji_pager);
-    this.strip     = (PagerSlidingTabStrip)      v.findViewById(R.id.tabs);
+    this.container = (LinearLayout)         v.findViewById(R.id.container);
+    this.pager     = (ViewPager)            v.findViewById(R.id.emoji_pager);
+    this.strip     = (PagerSlidingTabStrip) v.findViewById(R.id.tabs);
 
     RepeatableImageKey backspace = (RepeatableImageKey)v.findViewById(R.id.backspace);
     backspace.setOnKeyEventListener(new KeyEventListener() {
       @Override public void onKeyEvent() {
-        if (composeText != null && composeText.getText().length() > 0) {
-          composeText.dispatchKeyEvent(DELETE_KEY_EVENT);
-        }
+        if (listener != null) listener.onKeyEvent(DELETE_KEY_EVENT);
       }
     });
   }
 
-  public void hide() {
-    container.setVisibility(View.GONE);
-  }
-
-  public void show() {
-    int keyboardHeight = container.getKeyboardHeight();
-    Log.w("EmojiDrawer", "setting emoji drawer to height " + keyboardHeight);
-    container.setLayoutParams(new LinearLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, keyboardHeight));
-    container.requestLayout();
-    container.setVisibility(View.VISIBLE);
-  }
-
   public boolean isOpen() {
     return container.getVisibility() == View.VISIBLE;
   }
@@ -102,7 +81,7 @@ public class EmojiDrawer extends KeyboardAwareLinearLayout {
                                            new EmojiSelectionListener() {
                                              @Override public void onEmojiSelected(String emoji) {
                                                recentModel.onCodePointSelected(emoji);
-                                               composeText.insertEmoji(emoji);
+                                               if (listener != null) listener.onEmojiSelected(emoji);
                                              }
                                            }));
 
@@ -170,4 +149,8 @@ public class EmojiDrawer extends KeyboardAwareLinearLayout {
       return image;
     }
   }
+
+  public interface EmojiEventListener extends EmojiSelectionListener {
+    void onKeyEvent(KeyEvent keyEvent);
+  }
 }
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java
new file mode 100644
index 0000000000..0423844df1
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java
@@ -0,0 +1,31 @@
+package org.thoughtcrime.securesms.components.emoji;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.PopupWindow;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener;
+
+public class EmojiPopup extends PopupWindow {
+  private View parent;
+
+  public EmojiPopup(View parent) {
+    super(new EmojiDrawer(parent.getContext()),
+          parent.getWidth(),
+          parent.getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height));
+    this.parent = parent;
+  }
+
+  public void setEmojiEventListener(EmojiEventListener listener) {
+    ((EmojiDrawer)getContentView()).setEmojiEventListener(listener);
+  }
+
+  public void show(int height) {
+    setHeight(height);
+    showAtLocation(parent, Gravity.BOTTOM | Gravity.LEFT, 0, 0);
+  }
+}
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java
index 2d969a1b9f..554fd34b5c 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java
@@ -5,7 +5,6 @@ import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.ImageButton;
 
 import org.thoughtcrime.securesms.R;
@@ -14,7 +13,6 @@ public class EmojiToggle extends ImageButton {
 
   private Drawable emojiToggle;
   private Drawable imeToggle;
-  private OnClickListener listener;
 
   public EmojiToggle(Context context) {
     super(context);
@@ -31,46 +29,23 @@ public class EmojiToggle extends ImageButton {
     initialize();
   }
 
-  @Override
-  public void setOnClickListener(OnClickListener listener) {
-    this.listener = listener;
+  public void setToEmoji() {
+    setImageDrawable(emojiToggle);
   }
 
-  public void toggle() {
-    if (getDrawable() == emojiToggle) {
-      setImageDrawable(imeToggle);
-    } else {
-      setImageDrawable(emojiToggle);
-    }
+  public void setToIme() {
+    setImageDrawable(imeToggle);
   }
 
   private void initialize() {
-    initializeResources();
-    initializeListeners();
-  }
-
-  private void initializeResources() {
     int attributes[] = new int[] {R.attr.conversation_emoji_toggle,
-                                  R.attr.conversation_keyboard_toggle};
+        R.attr.conversation_keyboard_toggle};
 
     TypedArray drawables = getContext().obtainStyledAttributes(attributes);
     this.emojiToggle     = drawables.getDrawable(0);
     this.imeToggle       = drawables.getDrawable(1);
 
     drawables.recycle();
-
-    setImageDrawable(this.emojiToggle);
-  }
-
-  private void initializeListeners() {
-    super.setOnClickListener(new OnClickListener() {
-      @Override
-      public void onClick(View v) {
-        toggle();
-
-        if (listener != null)
-          listener.onClick(v);
-      }
-    });
+    setToEmoji();
   }
 }