mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-27 23:10:46 +00:00
parent
20a1507f7a
commit
fa62b9bde2
@ -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>
|
|
||||||
|
@ -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>
|
@ -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>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user