clean up EmojiDrawer layout

Fixes #2940
Closes #3517
// FREEBIE
This commit is contained in:
Jake McGinty 2015-07-02 16:47:03 -07:00 committed by Moxie Marlinspike
parent 20a1507f7a
commit fa62b9bde2
8 changed files with 270 additions and 172 deletions

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="fill_parent" xmlns:tools="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/layout_container"
android:background="?conversation_background" android:layout_width="fill_parent"
android:orientation="vertical"> android:layout_height="fill_parent"
android:background="?conversation_background"
android:orientation="vertical">
<RelativeLayout <RelativeLayout android:layout_width="fill_parent"
android:id="@+id/layout_container" android:layout_height="fill_parent"
android:layout_width="fill_parent" android:layout_weight="1"
android:layout_height="fill_parent" android:orientation="vertical"
android:layout_weight="1" android:gravity="bottom">
android:orientation="vertical"
android:gravity="bottom">
<FrameLayout android:id="@+id/fragment_content" <FrameLayout android:id="@+id/fragment_content"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -141,11 +141,4 @@
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
</org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout>
<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>

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container" android:id="@+id/container"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout android:background="?emoji_tab_strip_background" <LinearLayout android:background="?emoji_tab_strip_background"
android:orientation="horizontal" android:orientation="horizontal"
@ -47,4 +47,4 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?emoji_background" /> android:background="?emoji_background" />
</org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout> </LinearLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<dimen name="emoji_drawer_size">32sp</dimen> <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_height">200dp</dimen>
<dimen name="emoji_drawer_item_padding">5dp</dimen> <dimen name="emoji_drawer_item_padding">5dp</dimen>
<dimen name="emoji_drawer_indicator_height">1.5dp</dimen> <dimen name="emoji_drawer_indicator_height">1.5dp</dimen>

View File

@ -23,7 +23,6 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
@ -35,11 +34,11 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -48,7 +47,6 @@ import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener; import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.view.ViewStub;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
@ -63,8 +61,11 @@ import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.components.AnimatingToggle; import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.components.ComposeText; 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.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.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; 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 public class ConversationActivity extends PassphraseRequiredActionBarActivity
implements ConversationFragment.ConversationFragmentListener, implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener, AttachmentManager.AttachmentListener,
RecipientsModifiedListener RecipientsModifiedListener,
OnKeyboardShownListener
{ {
private static final String TAG = ConversationActivity.class.getSimpleName(); 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 GROUP_EDIT = 5;
private static final int CAPTURE_PHOTO = 6; private static final int CAPTURE_PHOTO = 6;
private MasterSecret masterSecret; private MasterSecret masterSecret;
protected ComposeText composeText; protected ComposeText composeText;
private AnimatingToggle buttonToggle; private AnimatingToggle buttonToggle;
private SendButton sendButton; private SendButton sendButton;
private ImageButton attachButton; private ImageButton attachButton;
protected ConversationTitleView titleView; protected ConversationTitleView titleView;
private TextView charactersLeft; private TextView charactersLeft;
private ConversationFragment fragment; private ConversationFragment fragment;
private Button unblockButton; private Button unblockButton;
private View composePanel; private KeyboardAwareLinearLayout container;
private View composeBubble; private View composePanel;
private View composeBubble;
private AttachmentTypeSelectorAdapter attachmentAdapter; private AttachmentTypeSelectorAdapter attachmentAdapter;
private AttachmentManager attachmentManager; private AttachmentManager attachmentManager;
private BroadcastReceiver securityUpdateReceiver; private BroadcastReceiver securityUpdateReceiver;
private BroadcastReceiver groupUpdateReceiver; private BroadcastReceiver groupUpdateReceiver;
private Optional<EmojiDrawer> emojiDrawer = Optional.absent(); private Optional<EmojiPopup> emojiPopup = Optional.absent();
private EmojiToggle emojiToggle; private EmojiToggle emojiToggle;
private Recipients recipients; private Recipients recipients;
@ -360,13 +363,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (isEmojiDrawerOpen()) { if (isEmojiDrawerOpen()) {
getEmojiDrawer().hide(); hideEmojiPopup(false);
emojiToggle.toggle();
} else { } else {
super.onBackPressed(); super.onBackPressed();
} }
} }
@Override
public void onKeyboardShown() {
hideEmojiPopup(true);
}
//////// Event Handlers //////// Event Handlers
private void handleReturnToConversationList() { private void handleReturnToConversationList() {
@ -730,16 +737,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void initializeViews() { private void initializeViews() {
buttonToggle = (AnimatingToggle)findViewById(R.id.button_toggle); titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
sendButton = (SendButton) findViewById(R.id.send_button); buttonToggle = (AnimatingToggle) findViewById(R.id.button_toggle);
attachButton = (ImageButton) findViewById(R.id.attach_button); sendButton = (SendButton) findViewById(R.id.send_button);
composeText = (ComposeText) findViewById(R.id.embedded_text_editor); attachButton = (ImageButton) findViewById(R.id.attach_button);
charactersLeft = (TextView) findViewById(R.id.space_left); composeText = (ComposeText) findViewById(R.id.embedded_text_editor);
emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle); charactersLeft = (TextView) findViewById(R.id.space_left);
titleView = (ConversationTitleView) getSupportActionBar().getCustomView(); emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle);
unblockButton = (Button) findViewById(R.id.unblock_button); unblockButton = (Button) findViewById(R.id.unblock_button);
composePanel = findViewById(R.id.bottom_panel); composePanel = findViewById(R.id.bottom_panel);
composeBubble = findViewById(R.id.compose_bubble); 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}; int[] attributes = new int[]{R.attr.conversation_item_bubble_background};
TypedArray colors = obtainStyledAttributes(attributes); TypedArray colors = obtainStyledAttributes(attributes);
@ -797,16 +807,44 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setDisplayShowTitleEnabled(false);
} }
private EmojiDrawer getEmojiDrawer() { private EmojiPopup getEmojiPopup() {
if (emojiDrawer.isPresent()) return emojiDrawer.get(); if (!emojiPopup.isPresent()) {
EmojiDrawer emojiDrawer = (EmojiDrawer)((ViewStub)findViewById(R.id.emoji_drawer_stub)).inflate(); EmojiPopup emojiPopup = new EmojiPopup(getWindow().getDecorView());
emojiDrawer.setComposeEditText(composeText); emojiPopup.setEmojiEventListener(new EmojiEventListener() {
this.emojiDrawer = Optional.of(emojiDrawer); @Override public void onKeyEvent(KeyEvent keyEvent) {
return emojiDrawer; 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() { private boolean isEmojiDrawerOpen() {
return emojiDrawer.isPresent() && emojiDrawer.get().isOpen(); return emojiPopup.isPresent() && emojiPopup.get().isShowing();
} }
private void initializeResources() { private void initializeResources() {
@ -1034,10 +1072,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void calculateCharactersRemaining() { private void calculateCharactersRemaining() {
int charactersSpent = composeText.getText().toString().length(); int charactersSpent = composeText.getText().toString().length();
TransportOption transportOption = sendButton.getSelectedTransport(); TransportOption transportOption = sendButton.getSelectedTransport();
CharacterState characterState = transportOption.calculateCharacters(charactersSpent);
CharacterState characterState = transportOption.calculateCharacters(charactersSpent);
if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) { if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) {
charactersLeft.setText(characterState.charactersRemaining + "/" + characterState.maxMessageSize charactersLeft.setText(characterState.charactersRemaining + "/" + characterState.maxMessageSize
@ -1235,12 +1272,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
if (isEmojiDrawerOpen()) { if (isEmojiDrawerOpen()) {
hideEmojiPopup(true);
input.showSoftInput(composeText, 0); input.showSoftInput(composeText, 0);
getEmojiDrawer().hide();
} else { } else {
container.postOnKeyboardClose(new Runnable() {
@Override public void run() {
showEmojiPopup();
}
});
input.hideSoftInputFromWindow(composeText.getWindowToken(), 0); input.hideSoftInputFromWindow(composeText.getWindowToken(), 0);
getEmojiDrawer().show();
} }
} }
} }
@ -1288,9 +1328,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (isEmojiDrawerOpen()) { hideEmojiPopup(true);
emojiToggle.performClick();
}
} }
@Override @Override
@ -1317,8 +1355,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onFocusChange(View v, boolean hasFocus) { public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus && isEmojiDrawerOpen()) { if (hasFocus) {
emojiToggle.performClick(); hideEmojiPopup(true);
} }
} }
} }

View File

@ -16,68 +16,84 @@
*/ */
package org.thoughtcrime.securesms.components; package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v7.widget.LinearLayoutCompat;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.LinearLayout;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import java.lang.reflect.Field; 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 * LinearLayout that, when a view container, will report back when it thinks a soft keyboard
* has been opened and what its height would be. * has been opened and what its height would be.
*/ */
public class KeyboardAwareLinearLayout extends LinearLayout { public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
private static final String TAG = KeyboardAwareLinearLayout.class.getSimpleName(); private static final String TAG = KeyboardAwareLinearLayout.class.getSimpleName();
private static final Rect rect = new Rect();
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) { public KeyboardAwareLinearLayout(Context context) {
super(context); this(context, null);
} }
public KeyboardAwareLinearLayout(Context context, AttributeSet attrs) { 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) { public KeyboardAwareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
minKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_keyboard_size);
} }
/** @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
* inspired by http://stackoverflow.com/a/7104303 Log.w(TAG, String.format("onMeasure(%s, %s)", MeasureSpec.toString(widthMeasureSpec), MeasureSpec.toString(heightMeasureSpec)));
* @param widthMeasureSpec width measure super.onMeasure(widthMeasureSpec, heightMeasureSpec);
* @param heightMeasureSpec height measure
*/ int res = getResources().getIdentifier("status_bar_height", "dimen", "android");
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int res = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight = res > 0 ? getResources().getDimensionPixelSize(res) : 0; int statusBarHeight = res > 0 ? getResources().getDimensionPixelSize(res) : 0;
final int availableHeight = this.getRootView().getHeight() - statusBarHeight - getViewInset(); 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)) { if (keyboardHeight - oldKeyboardHeight > minKeyboardSize && !keyboardOpen) {
onKeyboardShown(keyboardHeight); onKeyboardOpen(keyboardHeight);
} else if (oldKeyboardHeight - keyboardHeight > minKeyboardSize && keyboardOpen) {
onKeyboardClose();
} }
super.onMeasure(widthMeasureSpec, heightMeasureSpec); oldRect.set(newRect);
} }
public int getViewInset() { public void padForCustomKeyboard(int height) {
setPadding(0, 0, 0, height);
}
public void unpadForCustomKeyboard() {
setPadding(0, 0, 0, 0);
}
private int getViewInset() {
if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
return 0; return 0;
} }
@ -100,8 +116,9 @@ public class KeyboardAwareLinearLayout extends LinearLayout {
return 0; return 0;
} }
protected void onKeyboardShown(int keyboardHeight) { protected void onKeyboardOpen(int keyboardHeight) {
Log.w(TAG, "keyboard shown, height " + keyboardHeight); keyboardOpen = true;
Log.w(TAG, "onKeyboardOpen(" + keyboardHeight + ")");
WindowManager wm = (WindowManager) getContext().getSystemService(Activity.WINDOW_SERVICE); WindowManager wm = (WindowManager) getContext().getSystemService(Activity.WINDOW_SERVICE);
if (wm == null || wm.getDefaultDisplay() == null) { if (wm == null || wm.getDefaultDisplay() == null) {
@ -118,10 +135,22 @@ public class KeyboardAwareLinearLayout extends LinearLayout {
case Surface.ROTATION_180: case Surface.ROTATION_180:
setKeyboardPortraitHeight(keyboardHeight); setKeyboardPortraitHeight(keyboardHeight);
} }
notifyShownListeners();
unpadForCustomKeyboard();
}
protected void onKeyboardClose() {
keyboardOpen = false;
Log.w(TAG, "onKeyboardClose()");
notifyHiddenListeners();
}
public boolean isKeyboardOpen() {
return keyboardOpen;
} }
public int getKeyboardHeight() { 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) { if (wm == null || wm.getDefaultDisplay() == null) {
throw new AssertionError("WindowManager was null or there is no default display"); 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(); .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();
}
} }

View File

@ -5,6 +5,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v7.widget.LinearLayoutCompat;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -14,12 +15,10 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ImageView.ScaleType; import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.astuetz.PagerSlidingTabStrip; import com.astuetz.PagerSlidingTabStrip;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
import org.thoughtcrime.securesms.components.RepeatableImageKey; import org.thoughtcrime.securesms.components.RepeatableImageKey;
import org.thoughtcrime.securesms.components.RepeatableImageKey.KeyEventListener; import org.thoughtcrime.securesms.components.RepeatableImageKey.KeyEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiPageView.EmojiSelectionListener; 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.LinkedList;
import java.util.List; 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 static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
private EmojiEditText composeText; private LinearLayout container;
private KeyboardAwareLinearLayout container; private ViewPager pager;
private ViewPager pager; private List<EmojiPageModel> models;
private List<EmojiPageModel> models; private PagerSlidingTabStrip strip;
private PagerSlidingTabStrip strip; private RecentEmojiPageModel recentModel;
private RecentEmojiPageModel recentModel; private EmojiEventListener listener;
public EmojiDrawer(Context context) { public EmojiDrawer(Context context) {
super(context); this(context, null);
init();
} }
public EmojiDrawer(Context context, AttributeSet attrs) { public EmojiDrawer(Context context, AttributeSet attrs) {
super(context, attrs); this(context, attrs, 0);
init();
} }
public EmojiDrawer(Context context, AttributeSet attrs, int defStyle) { public EmojiDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, 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); final View v = LayoutInflater.from(getContext()).inflate(R.layout.emoji_drawer, this, true);
initializeResources(v); initializeResources(v);
initializePageModels(); initializePageModels();
initializeEmojiGrid(); initializeEmojiGrid();
} }
public void setEmojiEventListener(EmojiEventListener listener) {
this.listener = listener;
}
private void initializeResources(View v) { private void initializeResources(View v) {
Log.w("EmojiDrawer", "initializeResources()"); Log.w("EmojiDrawer", "initializeResources()");
this.container = (KeyboardAwareLinearLayout) v.findViewById(R.id.container); this.container = (LinearLayout) v.findViewById(R.id.container);
this.pager = (ViewPager) v.findViewById(R.id.emoji_pager); this.pager = (ViewPager) v.findViewById(R.id.emoji_pager);
this.strip = (PagerSlidingTabStrip) v.findViewById(R.id.tabs); this.strip = (PagerSlidingTabStrip) v.findViewById(R.id.tabs);
RepeatableImageKey backspace = (RepeatableImageKey)v.findViewById(R.id.backspace); RepeatableImageKey backspace = (RepeatableImageKey)v.findViewById(R.id.backspace);
backspace.setOnKeyEventListener(new KeyEventListener() { backspace.setOnKeyEventListener(new KeyEventListener() {
@Override public void onKeyEvent() { @Override public void onKeyEvent() {
if (composeText != null && composeText.getText().length() > 0) { if (listener != null) listener.onKeyEvent(DELETE_KEY_EVENT);
composeText.dispatchKeyEvent(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() { public boolean isOpen() {
return container.getVisibility() == View.VISIBLE; return container.getVisibility() == View.VISIBLE;
} }
@ -102,7 +81,7 @@ public class EmojiDrawer extends KeyboardAwareLinearLayout {
new EmojiSelectionListener() { new EmojiSelectionListener() {
@Override public void onEmojiSelected(String emoji) { @Override public void onEmojiSelected(String emoji) {
recentModel.onCodePointSelected(emoji); recentModel.onCodePointSelected(emoji);
composeText.insertEmoji(emoji); if (listener != null) listener.onEmojiSelected(emoji);
} }
})); }));
@ -170,4 +149,8 @@ public class EmojiDrawer extends KeyboardAwareLinearLayout {
return image; return image;
} }
} }
public interface EmojiEventListener extends EmojiSelectionListener {
void onKeyEvent(KeyEvent keyEvent);
}
} }

View File

@ -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);
}
}

View File

@ -5,7 +5,6 @@ import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -14,7 +13,6 @@ public class EmojiToggle extends ImageButton {
private Drawable emojiToggle; private Drawable emojiToggle;
private Drawable imeToggle; private Drawable imeToggle;
private OnClickListener listener;
public EmojiToggle(Context context) { public EmojiToggle(Context context) {
super(context); super(context);
@ -31,46 +29,23 @@ public class EmojiToggle extends ImageButton {
initialize(); initialize();
} }
@Override public void setToEmoji() {
public void setOnClickListener(OnClickListener listener) { setImageDrawable(emojiToggle);
this.listener = listener;
} }
public void toggle() { public void setToIme() {
if (getDrawable() == emojiToggle) { setImageDrawable(imeToggle);
setImageDrawable(imeToggle);
} else {
setImageDrawable(emojiToggle);
}
} }
private void initialize() { private void initialize() {
initializeResources();
initializeListeners();
}
private void initializeResources() {
int attributes[] = new int[] {R.attr.conversation_emoji_toggle, int attributes[] = new int[] {R.attr.conversation_emoji_toggle,
R.attr.conversation_keyboard_toggle}; R.attr.conversation_keyboard_toggle};
TypedArray drawables = getContext().obtainStyledAttributes(attributes); TypedArray drawables = getContext().obtainStyledAttributes(attributes);
this.emojiToggle = drawables.getDrawable(0); this.emojiToggle = drawables.getDrawable(0);
this.imeToggle = drawables.getDrawable(1); this.imeToggle = drawables.getDrawable(1);
drawables.recycle(); drawables.recycle();
setToEmoji();
setImageDrawable(this.emojiToggle);
}
private void initializeListeners() {
super.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
toggle();
if (listener != null)
listener.onClick(v);
}
});
} }
} }