diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b05db94018..d333d0e4eb 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -3,6 +3,7 @@
     <dimen name="emoji_drawer_size">32sp</dimen>
     <dimen name="min_keyboard_size">50dp</dimen>
     <dimen name="min_emoji_drawer_height">200dp</dimen>
+    <dimen name="min_emoji_drawer_top_margin">170dp</dimen>
     <dimen name="emoji_drawer_item_padding">5dp</dimen>
     <dimen name="emoji_drawer_indicator_height">1.5dp</dimen>
     <dimen name="emoji_drawer_left_right_padding">5dp</dimen>
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index e585fa15d4..a2f81bf938 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -23,6 +23,7 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
@@ -113,6 +114,7 @@ import org.thoughtcrime.securesms.util.DirectoryHelper;
 import org.thoughtcrime.securesms.util.DynamicLanguage;
 import org.thoughtcrime.securesms.util.DynamicTheme;
 import org.thoughtcrime.securesms.util.GroupUtil;
+import org.thoughtcrime.securesms.util.ServiceUtil;
 import org.thoughtcrime.securesms.util.TextSecurePreferences;
 import org.thoughtcrime.securesms.util.Util;
 import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
@@ -264,8 +266,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
 
   @Override public void onConfigurationChanged(Configuration newConfig) {
     super.onConfigurationChanged(newConfig);
-    Log.w(TAG, String.format("onConfigurationChanged(%d -> %d)", getResources().getConfiguration().orientation, newConfig.orientation));
     quickAttachmentDrawer.onConfigurationChanged();
+    hideEmojiPopup(false);
   }
 
   @Override
@@ -274,6 +276,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
     if (recipients != null)             recipients.removeListener(this);
     if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
     if (groupUpdateReceiver != null)    unregisterReceiver(groupUpdateReceiver);
+    hideEmojiPopup(false);
     super.onDestroy();
   }
 
@@ -858,13 +861,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
 
   private EmojiPopup getEmojiPopup() {
     if (!emojiPopup.isPresent()) {
-      EmojiPopup emojiPopup = new EmojiPopup(getWindow().getDecorView());
+      EmojiPopup emojiPopup = new EmojiPopup(container);
       emojiPopup.setEmojiEventListener(new EmojiEventListener() {
         @Override public void onKeyEvent(KeyEvent keyEvent) {
           composeText.dispatchKeyEvent(keyEvent);
         }
 
         @Override public void onEmojiSelected(String emoji) {
+          Log.w(TAG, "onEmojiSelected()");
           composeText.insertEmoji(emoji);
         }
       });
@@ -874,17 +878,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
   }
 
   private void showEmojiPopup() {
-    int height = Math.max(getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height),
-                          container.getKeyboardHeight());
-    container.padForCustomKeyboard(height);
-    getEmojiPopup().show(height);
+    getEmojiPopup().show();
     emojiToggle.setToIme();
   }
 
-  private void hideEmojiPopup(boolean expectingKeyboard) {
+  protected void hideEmojiPopup(boolean expectingKeyboard) {
     if (isEmojiDrawerOpen()) {
       getEmojiPopup().dismiss();
-      if (!expectingKeyboard) {
+      if (!expectingKeyboard || container.isLandscape()) {
         container.unpadForCustomKeyboard();
       }
     }
@@ -1330,22 +1331,34 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
     }
   }
 
+  private void openKeyboardForComposition() {
+    composeText.post(new Runnable() {
+      @Override public void run() {
+        composeText.requestFocus();
+        ServiceUtil.getInputMethodManager(ConversationActivity.this).showSoftInput(composeText, 0);
+      }
+    });
+  }
+
+  private void hideKeyboard() {
+    ServiceUtil.getInputMethodManager(this).hideSoftInputFromWindow(composeText.getWindowToken(), 0);
+  }
+
   private class EmojiToggleListener implements OnClickListener {
     @Override
     public void onClick(View v) {
-      InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
-
+      Log.w(TAG, "EmojiToggleListener onClick()");
       if (isEmojiDrawerOpen()) {
         hideEmojiPopup(true);
-        input.showSoftInput(composeText, 0);
+        openKeyboardForComposition();
       } else {
         container.postOnKeyboardClose(new Runnable() {
           @Override public void run() {
             showEmojiPopup();
           }
         });
-        input.hideSoftInputFromWindow(composeText.getWindowToken(), 0);
-        quickAttachmentDrawer.setDrawerStateAndAnimate(DrawerState.COLLAPSED);
+        quickAttachmentDrawer.close();
+        hideKeyboard();
       }
     }
   }
diff --git a/src/org/thoughtcrime/securesms/ConversationPopupActivity.java b/src/org/thoughtcrime/securesms/ConversationPopupActivity.java
index 4c16eb71d9..96d79d7862 100644
--- a/src/org/thoughtcrime/securesms/ConversationPopupActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationPopupActivity.java
@@ -112,6 +112,11 @@ public class ConversationPopupActivity extends ConversationActivity {
     getSupportActionBar().setDisplayHomeAsUpEnabled(false);
   }
 
+  @Override
+  protected void hideEmojiPopup(boolean expectingKeyboard) {
+    super.hideEmojiPopup(false);
+  }
+
   @Override
   protected void sendComplete(long threadId) {
     super.sendComplete(threadId);
diff --git a/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java b/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
index 5590ac4ba9..264a84c32e 100644
--- a/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
+++ b/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
@@ -16,7 +16,6 @@
  */
 package org.thoughtcrime.securesms.components;
 
-import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Build;
@@ -27,9 +26,9 @@ import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Surface;
 import android.view.View;
-import android.view.WindowManager;
 
 import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.util.ServiceUtil;
 
 import java.lang.reflect.Field;
 import java.util.HashSet;
@@ -46,9 +45,10 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
   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 final int minKeyboardSize;
 
-  private boolean keyboardOpen;
+  private boolean keyboardOpen = false;
+  private int     rotation     = -1;
 
   public KeyboardAwareLinearLayout(Context context) {
     this(context, null);
@@ -64,16 +64,29 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
   }
 
   @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    updateRotation();
+    updateKeyboardState();
     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+  }
 
-    int res             = getResources().getIdentifier("status_bar_height", "dimen", "android");
+  private void updateRotation() {
+    int oldRotation = rotation;
+    rotation = getDeviceRotation();
+    if (oldRotation != rotation) {
+      onKeyboardClose();
+      oldRect.setEmpty();
+    }
+  }
+
+  private void updateKeyboardState() {
+    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(newRect);
 
     final int oldKeyboardHeight = availableHeight - (oldRect.bottom - oldRect.top);
-    final int keyboardHeight    = availableHeight - (newRect.bottom - newRect.top);
+    final int keyboardHeight = availableHeight - (newRect.bottom - newRect.top);
 
     if (keyboardHeight - oldKeyboardHeight > minKeyboardSize && !keyboardOpen) {
       onKeyboardOpen(keyboardHeight);
@@ -84,7 +97,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
     oldRect.set(newRect);
   }
 
-  public void padForCustomKeyboard(int height) {
+  public void padForCustomKeyboard(final int height) {
     setPadding(0, 0, 0, height);
   }
 
@@ -117,22 +130,9 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
 
   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) {
-      return;
-    }
-    int rotation = wm.getDefaultDisplay().getRotation();
-
-    switch (rotation) {
-      case Surface.ROTATION_270:
-      case Surface.ROTATION_90:
-        setKeyboardLandscapeHeight(keyboardHeight);
-        break;
-      case Surface.ROTATION_0:
-      case Surface.ROTATION_180:
-        setKeyboardPortraitHeight(keyboardHeight);
+    if (!isLandscape()) {
+      setKeyboardPortraitHeight(keyboardHeight);
     }
     notifyShownListeners();
     unpadForCustomKeyboard();
@@ -140,7 +140,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
 
   protected void onKeyboardClose() {
     keyboardOpen = false;
-    Log.w(TAG, "onKeyboardClose()");
     notifyHiddenListeners();
   }
 
@@ -149,39 +148,26 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
   }
 
   public int getKeyboardHeight() {
-    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");
-    }
+    return isLandscape() ? getKeyboardLandscapeHeight() : getKeyboardPortraitHeight();
+  }
 
-    int rotation = wm.getDefaultDisplay().getRotation();
-
-    switch (rotation) {
-      case Surface.ROTATION_270:
-      case Surface.ROTATION_90:
-        return getKeyboardLandscapeHeight();
-      case Surface.ROTATION_0:
-      case Surface.ROTATION_180:
-      default:
-        return getKeyboardPortraitHeight();
-    }
+  public boolean isLandscape() {
+    int rotation = getDeviceRotation();
+    return rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270;
+  }
+  private int getDeviceRotation() {
+    return ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRotation();
   }
 
   private int getKeyboardLandscapeHeight() {
-    return PreferenceManager.getDefaultSharedPreferences(getContext())
-                            .getInt("keyboard_height_landscape",
-                                    getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height));
+    return Math.max(getHeight(), getRootView().getHeight()) / 2;
   }
 
   private int getKeyboardPortraitHeight() {
-    return PreferenceManager.getDefaultSharedPreferences(getContext())
-                            .getInt("keyboard_height_portrait",
-                                    getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height));
-  }
-
-  private void setKeyboardLandscapeHeight(int height) {
-    PreferenceManager.getDefaultSharedPreferences(getContext())
-                     .edit().putInt("keyboard_height_landscape", height).apply();
+    int keyboardHeight = PreferenceManager.getDefaultSharedPreferences(getContext())
+                                          .getInt("keyboard_height_portrait",
+                                                  getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height));
+    return Math.min(keyboardHeight, getRootView().getHeight() - getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_top_margin));
   }
 
   private void setKeyboardPortraitHeight(int height) {
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
index 26695412d4..1e6eb8f16b 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
@@ -80,6 +80,7 @@ public class EmojiDrawer extends LinearLayoutCompat {
                                            models,
                                            new EmojiSelectionListener() {
                                              @Override public void onEmojiSelected(String emoji) {
+                                               Log.w("EmojiDrawer", "onEmojiSelected()");
                                                recentModel.onCodePointSelected(emoji);
                                                if (listener != null) listener.onEmojiSelected(emoji);
                                              }
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java
index 1147c63a77..7046d1ce08 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java
@@ -1,16 +1,12 @@
 package org.thoughtcrime.securesms.components.emoji;
 
-import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION_CODES;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.GridView;
@@ -25,24 +21,27 @@ public class EmojiPageView extends FrameLayout {
   private GridView               grid;
 
   public EmojiPageView(Context context) {
-    super(context);
-    init();
+    this(context, null);
   }
 
   public EmojiPageView(Context context, AttributeSet attrs) {
-    super(context, attrs);
-    init();
+    this(context, attrs, 0);
   }
 
   public EmojiPageView(Context context, AttributeSet attrs, int defStyleAttr) {
     super(context, attrs, defStyleAttr);
-    init();
-  }
-
-  @TargetApi(VERSION_CODES.LOLLIPOP)
-  public EmojiPageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-    super(context, attrs, defStyleAttr, defStyleRes);
-    init();
+    final View view = LayoutInflater.from(getContext()).inflate(R.layout.emoji_grid_layout, this, true);
+    grid = (GridView) view.findViewById(R.id.emoji);
+    grid.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2 * getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding));
+    grid.setOnTouchListener(new OnTouchListener() {
+      @Override public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_UP) {
+          EmojiView emojiView = (EmojiView)grid.getChildAt(grid.pointToPosition((int)event.getX(), (int)event.getY()));
+          if (listener != null && emojiView != null) listener.onEmojiSelected(emojiView.getEmoji());
+        }
+        return false;
+      }
+    });
   }
 
   public void onSelected() {
@@ -51,17 +50,6 @@ public class EmojiPageView extends FrameLayout {
     }
   }
 
-  private void init() {
-    final View view = LayoutInflater.from(getContext()).inflate(R.layout.emoji_grid_layout, this, true);
-    grid = (GridView) view.findViewById(R.id.emoji);
-    grid.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2 * getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding));
-    grid.setOnItemClickListener(new OnItemClickListener() {
-      @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        if (listener != null) listener.onEmojiSelected((String)view.getTag());
-      }
-    });
-  }
-
   public void setModel(EmojiPageModel model) {
     this.model = model;
     grid.setAdapter(new EmojiGridAdapter(getContext(), model));
@@ -73,9 +61,9 @@ public class EmojiPageView extends FrameLayout {
 
   private static class EmojiGridAdapter extends BaseAdapter {
 
-    protected final Context        context;
-    private   final int            emojiSize;
-    private   final EmojiPageModel model;
+    protected final Context                context;
+    private   final int                    emojiSize;
+    private   final EmojiPageModel         model;
 
     public EmojiGridAdapter(Context context, EmojiPageModel model) {
       this.context   = context;
@@ -104,14 +92,13 @@ public class EmojiPageView extends FrameLayout {
       if (convertView != null && convertView instanceof EmojiView) {
         view = (EmojiView)convertView;
       } else {
-        EmojiView emojiView = new EmojiView(context);
+        final EmojiView emojiView = new EmojiView(context);
         emojiView.setPadding(pad, pad, pad, pad);
         emojiView.setLayoutParams(new AbsListView.LayoutParams(emojiSize + 2 * pad, emojiSize + 2 * pad));
         view = emojiView;
       }
 
       view.setEmoji(model.getEmoji()[position]);
-      view.setTag(model.getEmoji()[position]);
       return view;
     }
   }
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java
index 0423844df1..b9ed9c6eef 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiPopup.java
@@ -1,31 +1,45 @@
 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.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.PopupWindow;
 
 import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
 import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener;
 
 public class EmojiPopup extends PopupWindow {
-  private View parent;
+  private static final String TAG = EmojiPopup.class.getSimpleName();
+  private KeyboardAwareLinearLayout parent;
 
-  public EmojiPopup(View parent) {
+  public EmojiPopup(KeyboardAwareLinearLayout parent) {
     super(new EmojiDrawer(parent.getContext()),
           parent.getWidth(),
           parent.getResources().getDimensionPixelSize(R.dimen.min_emoji_drawer_height));
     this.parent = parent;
+    Log.w("EmojiPopup", "popup initialized with width " + parent.getWidth());
   }
 
   public void setEmojiEventListener(EmojiEventListener listener) {
     ((EmojiDrawer)getContentView()).setEmojiEventListener(listener);
   }
 
-  public void show(int height) {
-    setHeight(height);
+  public void show() {
+    setHeight(parent.getKeyboardHeight());
+    setWidth(parent.getWidth());
+    parent.padForCustomKeyboard(getHeight());
+    Log.w(TAG, String.format("show(%d, %d)", getWidth(), getHeight()));
     showAtLocation(parent, Gravity.BOTTOM | Gravity.LEFT, 0, 0);
   }
+
+  @Override
+  public void dismiss() {
+    super.dismiss();
+  }
+
+  public void update() {
+    update(parent, 0, 0, parent.getWidth(), -1);
+  }
 }
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiView.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiView.java
index 7d35dee120..b464cc52ec 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiView.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiView.java
@@ -23,22 +23,17 @@ public class EmojiView extends View implements Drawable.Callback {
   private final Rect  textBounds = new Rect();
 
   public EmojiView(Context context) {
-    super(context);
+    this(context, null);
   }
 
   public EmojiView(Context context, AttributeSet attrs) {
-    super(context, attrs);
+    this(context, attrs, 0);
   }
 
   public EmojiView(Context context, AttributeSet attrs, int defStyleAttr) {
     super(context, attrs, defStyleAttr);
   }
 
-  @TargetApi(VERSION_CODES.LOLLIPOP)
-  public EmojiView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-    super(context, attrs, defStyleAttr, defStyleRes);
-  }
-
   public void setEmoji(String emoji) {
     this.emoji    = emoji;
     this.drawable = EmojiProvider.getInstance(getContext())
@@ -47,6 +42,10 @@ public class EmojiView extends View implements Drawable.Callback {
     postInvalidate();
   }
 
+  public String getEmoji() {
+    return emoji;
+  }
+
   @Override protected void onDraw(Canvas canvas) {
     if (drawable != null) {
       drawable.setBounds(getPaddingLeft(),
diff --git a/src/org/thoughtcrime/securesms/util/ServiceUtil.java b/src/org/thoughtcrime/securesms/util/ServiceUtil.java
new file mode 100644
index 0000000000..049433d437
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/util/ServiceUtil.java
@@ -0,0 +1,16 @@
+package org.thoughtcrime.securesms.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+
+public class ServiceUtil {
+  public static InputMethodManager getInputMethodManager(Context context) {
+    return (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
+  }
+
+  public static WindowManager getWindowManager(Context context) {
+    return (WindowManager) context.getSystemService(Activity.WINDOW_SERVICE);
+  }
+}