diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index fde6002d97..abeb5b498d 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -1,72 +1,59 @@
-
+ android:layout_height="match_parent">
-
+
-
+
+ android:layout_height="0dp"
+ android:layout_weight="1" />
-
+
-
-
+
-
+
-
-
+
-
-
-
-
+
-
-
-
-
+
+
+
+
+
diff --git a/res/layout/emoji_drawer_stub.xml b/res/layout/emoji_drawer_stub.xml
deleted file mode 100644
index dd8612ec42..0000000000
--- a/res/layout/emoji_drawer_stub.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index 7fdde5e127..49d64a0c62 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -37,7 +37,6 @@ import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v4.view.WindowCompat;
import android.text.Editable;
-import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
@@ -48,9 +47,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
-import android.view.ViewStub;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
@@ -67,6 +64,8 @@ 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.camera.QuickAttachmentDrawer.DrawerState;
+import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
@@ -113,13 +112,11 @@ 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;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libaxolotl.InvalidMessageException;
-import org.whispersystems.libaxolotl.util.guava.Optional;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
@@ -161,24 +158,24 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int GROUP_EDIT = 5;
private static final int TAKE_PHOTO = 6;
- private MasterSecret masterSecret;
- protected ComposeText composeText;
- private AnimatingToggle buttonToggle;
- private SendButton sendButton;
- private ImageButton attachButton;
- protected ConversationTitleView titleView;
- private TextView charactersLeft;
- private ConversationFragment fragment;
- private Button unblockButton;
- private KeyboardAwareLinearLayout container;
- private View composePanel;
- private View composeBubble;
+ private MasterSecret masterSecret;
+ protected ComposeText composeText;
+ private AnimatingToggle buttonToggle;
+ private SendButton sendButton;
+ private ImageButton attachButton;
+ protected ConversationTitleView titleView;
+ private TextView charactersLeft;
+ private ConversationFragment fragment;
+ private Button unblockButton;
+ private InputAwareLayout container;
+ private View composePanel;
+ private View composeBubble;
private AttachmentTypeSelectorAdapter attachmentAdapter;
private AttachmentManager attachmentManager;
private BroadcastReceiver securityUpdateReceiver;
private BroadcastReceiver groupUpdateReceiver;
- private Optional emojiDrawer = Optional.absent();
+ private EmojiDrawer emojiDrawer;
private EmojiToggle emojiToggle;
protected HidingImageButton quickAttachmentToggle;
private QuickAttachmentDrawer quickAttachmentDrawer;
@@ -265,19 +262,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
@Override public void onConfigurationChanged(Configuration newConfig) {
+ Log.w(TAG, "onConfigurationChanged(" + newConfig.orientation + ")");
super.onConfigurationChanged(newConfig);
composeText.setTransport(sendButton.getSelectedTransport());
quickAttachmentDrawer.onConfigurationChanged();
- hideEmojiDrawer(false);
+ if (container.getCurrentInput() == emojiDrawer) container.hideAttachedInput();
}
@Override
protected void onDestroy() {
saveDraft();
- if (recipients != null) recipients.removeListener(this);
+ if (recipients != null) recipients.removeListener(this);
if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
- if (groupUpdateReceiver != null) unregisterReceiver(groupUpdateReceiver);
- if (isEmojiDrawerOpen()) hideEmojiDrawer(false);
+ if (groupUpdateReceiver != null) unregisterReceiver(groupUpdateReceiver);
super.onDestroy();
}
@@ -386,19 +383,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onBackPressed() {
Log.w(TAG, "onBackPressed()");
- if (isEmojiDrawerOpen()) {
- Log.w(TAG, "hiding emoji popup");
- hideEmojiDrawer(false);
- } else if (quickAttachmentDrawer.isOpen()) {
- quickAttachmentDrawer.close();
- } else {
- super.onBackPressed();
- }
+ if (container.isInputOpen()) container.hideCurrentInput(composeText);
+ else super.onBackPressed();
}
@Override
public void onKeyboardShown() {
- hideEmojiDrawer(true);
+ emojiToggle.setToEmoji();
}
//////// Event Handlers
@@ -758,17 +749,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void initializeViews() {
- titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
- buttonToggle = (AnimatingToggle) findViewById(R.id.button_toggle);
- sendButton = (SendButton) findViewById(R.id.send_button);
- attachButton = (ImageButton) findViewById(R.id.attach_button);
- composeText = (ComposeText) findViewById(R.id.embedded_text_editor);
- charactersLeft = (TextView) findViewById(R.id.space_left);
- emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle);
- unblockButton = (Button) findViewById(R.id.unblock_button);
- composePanel = findViewById(R.id.bottom_panel);
- composeBubble = findViewById(R.id.compose_bubble);
- container = (KeyboardAwareLinearLayout) findViewById(R.id.layout_container);
+ titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
+ buttonToggle = (AnimatingToggle) findViewById(R.id.button_toggle);
+ sendButton = (SendButton) findViewById(R.id.send_button);
+ attachButton = (ImageButton) findViewById(R.id.attach_button);
+ composeText = (ComposeText) findViewById(R.id.embedded_text_editor);
+ charactersLeft = (TextView) findViewById(R.id.space_left);
+ emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle);
+ emojiDrawer = (EmojiDrawer) findViewById(R.id.emoji_drawer);
+ unblockButton = (Button) findViewById(R.id.unblock_button);
+ composePanel = findViewById(R.id.bottom_panel);
+ composeBubble = findViewById(R.id.compose_bubble);
+ container = (InputAwareLayout) findViewById(R.id.layout_container);
container.addOnKeyboardShownListener(this);
@@ -780,7 +772,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle);
titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
unblockButton = (Button) findViewById(R.id.unblock_button);
- composePanel = findViewById(R.id.bottom_panel);
+ composePanel = findViewById(R.id.bottom_panel);
quickAttachmentDrawer = (QuickAttachmentDrawer) findViewById(R.id.quick_attachment_drawer);
quickAttachmentToggle = (HidingImageButton) findViewById(R.id.quick_attachment_toggle);
@@ -796,6 +788,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
SendButtonListener sendButtonListener = new SendButtonListener();
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
+ emojiToggle.attach(emojiDrawer);
+ emojiToggle.setOnClickListener(new EmojiToggleListener());
+ emojiDrawer.setEmojiEventListener(new EmojiEventListener() {
+ @Override public void onKeyEvent(KeyEvent keyEvent) {
+ composeText.dispatchKeyEvent(keyEvent);
+ }
+
+ @Override public void onEmojiSelected(String emoji) {
+ composeText.insertEmoji(emoji);
+ }
+ });
+
composeText.setOnEditorActionListener(sendButtonListener);
attachButton.setOnClickListener(new AttachButtonListener());
sendButton.setOnClickListener(sendButtonListener);
@@ -831,7 +835,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.setOnEditorActionListener(sendButtonListener);
composeText.setOnClickListener(composeKeyPressedListener);
composeText.setOnFocusChangeListener(composeKeyPressedListener);
- emojiToggle.setOnClickListener(new EmojiToggleListener());
if (QuickAttachmentDrawer.isDeviceSupported(this)) {
quickAttachmentDrawer.setListener(this);
@@ -848,48 +851,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
- private EmojiDrawer getEmojiDrawer() {
- if (!emojiDrawer.isPresent()) {
- EmojiDrawer emojiDrawer = (EmojiDrawer)((ViewStub)findViewById(R.id.emoji_drawer_stub)).inflate();
- emojiDrawer.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);
- }
- });
- this.emojiDrawer = Optional.of(emojiDrawer);
- }
- return emojiDrawer.get();
- }
-
- private void showEmojiDrawer() {
- getEmojiDrawer().show(container);
- emojiToggle.setToIme();
- }
-
- protected void hideEmojiDrawer(boolean expectingKeyboard) {
- if (isEmojiDrawerOpen()) {
- if (!expectingKeyboard || container.isLandscape()) {
- getEmojiDrawer().dismiss();
- } else {
- container.postOnKeyboardOpen(new Runnable() {
- @Override public void run() {
- getEmojiDrawer().dismiss();
- }
- });
- }
- }
- emojiToggle.setToEmoji();
- }
-
- private boolean isEmojiDrawerOpen() {
- return emojiDrawer.isPresent() && emojiDrawer.get().isShowing();
- }
-
private void initializeResources() {
recipients = RecipientFactory.getRecipientsForIds(this, getIntent().getLongArrayExtra(RECIPIENTS_EXTRA), true);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
@@ -1302,26 +1263,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
@Override
- public void onAttachmentDrawerClosed() {
- getSupportActionBar().show();
- }
-
- @Override
- public void onAttachmentDrawerOpened() {
- getSupportActionBar().hide();
+ public void onAttachmentDrawerStateChanged(DrawerState drawerState) {
+ if (drawerState == DrawerState.FULL_EXPANDED) {
+ getSupportActionBar().hide();
+ } else {
+ getSupportActionBar().show();
+ }
}
@Override
public void onImageCapture(@NonNull final byte[] imageBytes) {
attachmentManager.setCaptureUri(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes));
addAttachmentImage(masterSecret, attachmentManager.getCaptureUri());
- quickAttachmentDrawer.close();
+ quickAttachmentDrawer.hide(false);
}
@Override
public void onCameraFail(FailureReason reason) {
Toast.makeText(this, R.string.quick_camera_unavailable, Toast.LENGTH_SHORT).show();
- quickAttachmentDrawer.close();
+ quickAttachmentDrawer.hide(false);
quickAttachmentToggle.disable();
}
@@ -1335,46 +1295,19 @@ 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) {
- Log.w(TAG, "EmojiToggleListener onClick()");
- if (isEmojiDrawerOpen()) {
- hideEmojiDrawer(true);
- openKeyboardForComposition();
- } else {
- container.postOnKeyboardClose(new Runnable() {
- @Override public void run() {
- showEmojiDrawer();
- }
- });
- quickAttachmentDrawer.close();
- hideKeyboard();
- }
+
+ @Override public void onClick(View v) {
+ if (container.getCurrentInput() == emojiDrawer) container.showSoftkey(composeText);
+ else container.show(composeText, emojiDrawer);
}
}
private class QuickAttachmentToggleListener implements OnClickListener {
@Override
public void onClick(View v) {
- InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- input.hideSoftInputFromWindow(composeText.getWindowToken(), 0);
composeText.clearFocus();
- hideEmojiDrawer(false);
- quickAttachmentDrawer.open();
+ container.show(composeText, quickAttachmentDrawer);
}
}
@@ -1421,7 +1354,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onClick(View v) {
- hideEmojiDrawer(true);
+ container.showSoftkey(composeText);
}
@Override
@@ -1447,13 +1380,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onTextChanged(CharSequence s, int start, int before,int count) {}
@Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus && isEmojiDrawerOpen()) {
- hideEmojiDrawer(true);
- } else if (hasFocus && quickAttachmentDrawer.isOpen()) {
- quickAttachmentDrawer.close();
- }
- }
+ public void onFocusChange(View v, boolean hasFocus) {}
}
@Override
diff --git a/src/org/thoughtcrime/securesms/ConversationPopupActivity.java b/src/org/thoughtcrime/securesms/ConversationPopupActivity.java
index b165601008..b34efc4239 100644
--- a/src/org/thoughtcrime/securesms/ConversationPopupActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationPopupActivity.java
@@ -113,11 +113,6 @@ public class ConversationPopupActivity extends ConversationActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
- @Override
- protected void hideEmojiDrawer(boolean expectingKeyboard) {
- super.hideEmojiDrawer(false);
- }
-
@Override
protected void sendComplete(long threadId) {
super.sendComplete(threadId);
diff --git a/src/org/thoughtcrime/securesms/components/InputAwareLayout.java b/src/org/thoughtcrime/securesms/components/InputAwareLayout.java
new file mode 100644
index 0000000000..5436ec9150
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/InputAwareLayout.java
@@ -0,0 +1,94 @@
+package org.thoughtcrime.securesms.components;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
+import org.thoughtcrime.securesms.util.ServiceUtil;
+
+public class InputAwareLayout extends KeyboardAwareLinearLayout implements OnKeyboardShownListener {
+ private InputView current;
+
+ public InputAwareLayout(Context context) {
+ this(context, null);
+ }
+
+ public InputAwareLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public InputAwareLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ addOnKeyboardShownListener(this);
+ }
+
+ @Override public void onKeyboardShown() {
+ hideAttachedInput();
+ }
+
+ public void show(@NonNull final EditText imeTarget, @NonNull final InputView input) {
+ if (isKeyboardOpen()) {
+ hideSoftkey(imeTarget, new Runnable() {
+ @Override public void run() {
+ input.show(getKeyboardHeight(), true);
+ }
+ });
+ } else if (current != null && current.isShowing()) {
+ current.hide(true);
+ input.show(getKeyboardHeight(), true);
+ } else {
+ input.show(getKeyboardHeight(), false);
+ }
+
+ current = input;
+ }
+
+ public InputView getCurrentInput() {
+ return current;
+ }
+
+ public void hideCurrentInput(EditText imeTarget) {
+ if (isKeyboardOpen()) hideSoftkey(imeTarget, null);
+ else hideAttachedInput();
+ }
+
+ public void hideAttachedInput() {
+ if (current != null) current.hide(true);
+ current = null;
+ }
+
+ public boolean isInputOpen() {
+ return (isKeyboardOpen() || (current != null && current.isShowing()));
+ }
+
+ public void showSoftkey(final EditText inputTarget) {
+ postOnKeyboardOpen(new Runnable() {
+ @Override public void run() {
+ hideAttachedInput();
+ }
+ });
+ inputTarget.post(new Runnable() {
+ @Override public void run() {
+ inputTarget.requestFocus();
+ ServiceUtil.getInputMethodManager(inputTarget.getContext()).showSoftInput(inputTarget, 0);
+ }
+ });
+ }
+
+ private void hideSoftkey(final EditText inputTarget, @Nullable Runnable runAfterClose) {
+ if (runAfterClose != null) postOnKeyboardClose(runAfterClose);
+
+ ServiceUtil.getInputMethodManager(inputTarget.getContext())
+ .hideSoftInputFromWindow(inputTarget.getWindowToken(), 0);
+ }
+
+ public interface InputView {
+ void show(int height, boolean immediate);
+ void hide(boolean immediate);
+ boolean isShowing();
+ }
+}
+
diff --git a/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java b/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
index 200a93aeb0..d0ad8ed731 100644
--- a/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
+++ b/src/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java
@@ -16,6 +16,7 @@
*/
package org.thoughtcrime.securesms.components;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
@@ -42,14 +43,17 @@ import java.util.Set;
public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
private static final String TAG = KeyboardAwareLinearLayout.class.getSimpleName();
- private final Rect oldRect = new Rect();
- private final Rect newRect = new Rect();
- private final Set hiddenListeners = new HashSet<>();
- private final Set shownListeners = new HashSet<>();
- private final int minKeyboardSize;
- private final int minCustomKeyboardSize;
- private final int defaultCustomKeyboardSize;
- private final int minCustomKeyboardTopMargin;
+ private final Rect oldRect = new Rect();
+ private final Rect newRect = new Rect();
+ private final Set hiddenListeners = new HashSet<>();
+ private final Set shownListeners = new HashSet<>();
+ private final int minKeyboardSize;
+ private final int minCustomKeyboardSize;
+ private final int defaultCustomKeyboardSize;
+ private final int minCustomKeyboardTopMargin;
+ private final int statusBarHeight;
+
+ private int viewInset;
private boolean keyboardOpen = false;
private int rotation = -1;
@@ -64,16 +68,19 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
public KeyboardAwareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ final int statusBarRes = getResources().getIdentifier("status_bar_height", "dimen", "android");
minKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_keyboard_size);
minCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_size);
defaultCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.default_custom_keyboard_size);
minCustomKeyboardTopMargin = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin);
+ statusBarHeight = statusBarRes > 0 ? getResources().getDimensionPixelSize(statusBarRes) : 0;
+ viewInset = getViewInset();
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
updateRotation();
updateKeyboardState();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void updateRotation() {
@@ -86,10 +93,8 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
}
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();
+ if (viewInset == 0 && Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) viewInset = getViewInset();
+ final int availableHeight = this.getRootView().getHeight() - statusBarHeight - viewInset;
getWindowVisibleDisplayFrame(newRect);
final int oldKeyboardHeight = availableHeight - (oldRect.bottom - oldRect.top);
@@ -104,11 +109,8 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
oldRect.set(newRect);
}
+ @TargetApi(VERSION_CODES.LOLLIPOP)
private int getViewInset() {
- if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return 0;
- }
-
try {
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
attachInfoField.setAccessible(true);
@@ -141,6 +143,10 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
notifyHiddenListeners();
}
+ public boolean isKeyboardOpen() {
+ return keyboardOpen;
+ }
+
public int getKeyboardHeight() {
return isLandscape() ? getKeyboardLandscapeHeight() : getKeyboardPortraitHeight();
}
diff --git a/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java b/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java
index 9e6cf5de24..7d98262e85 100644
--- a/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java
+++ b/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java
@@ -21,32 +21,31 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.InputManager.InputView;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
import org.thoughtcrime.securesms.components.camera.QuickCamera.QuickCameraListener;
import org.thoughtcrime.securesms.util.ServiceUtil;
+import org.thoughtcrime.securesms.util.Util;
-public class QuickAttachmentDrawer extends ViewGroup {
- private static final String TAG = QuickAttachmentDrawer.class.getSimpleName();
- private static final float FULL_EXPANDED_ANCHOR_POINT = 1.f;
- private static final float COLLAPSED_ANCHOR_POINT = 0.f;
+public class QuickAttachmentDrawer extends ViewGroup implements InputView {
+ private static final String TAG = QuickAttachmentDrawer.class.getSimpleName();
private final ViewDragHelper dragHelper;
private QuickCamera quickCamera;
private int coverViewPosition;
- private KeyboardAwareLinearLayout coverView;
+ private KeyboardAwareLinearLayout container;
+ private View coverView;
private View controls;
private ImageButton fullScreenButton;
private ImageButton swapCameraButton;
private ImageButton shutterButton;
- private float slideOffset;
+ private int slideOffset;
private float initialMotionX;
private float initialMotionY;
private int rotation;
- private int slideRange;
private AttachmentDrawerListener listener;
private int halfExpandedHeight;
- private float halfExpandedAnchorPoint;
private DrawerState drawerState = DrawerState.COLLAPSED;
private boolean halfModeUnsupported = VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH;
@@ -82,16 +81,19 @@ public class QuickAttachmentDrawer extends ViewGroup {
Camera.getNumberOfCameras() > 0;
}
- public boolean isOpen() {
+ @Override
+ public boolean isShowing() {
return drawerState.isVisible();
}
- public void close() {
- setDrawerStateAndAnimate(DrawerState.COLLAPSED);
+ @Override
+ public void hide(boolean immediate) {
+ setDrawerStateAndUpdate(DrawerState.COLLAPSED, immediate);
}
- public void open() {
- setDrawerStateAndAnimate(DrawerState.HALF_EXPANDED);
+ @Override
+ public void show(int height, boolean immediate) {
+ setDrawerStateAndUpdate(DrawerState.HALF_EXPANDED, immediate);
}
public void onConfigurationChanged() {
@@ -99,11 +101,11 @@ public class QuickAttachmentDrawer extends ViewGroup {
final boolean rotationChanged = this.rotation != rotation;
this.rotation = rotation;
if (rotationChanged) {
- if (isOpen()) {
+ if (isShowing()) {
quickCamera.onPause();
}
updateControlsView();
- setDrawerStateAndAnimate(drawerState);
+ setDrawerStateAndUpdate(drawerState);
}
}
@@ -134,23 +136,19 @@ public class QuickAttachmentDrawer extends ViewGroup {
return isLandscape() || halfModeUnsupported;
}
- private KeyboardAwareLinearLayout getCoverView() {
- if (coverView != null) return coverView;
-
- final View coverViewChild = getChildAt(coverViewPosition);
- if (coverViewChild != null && !(coverViewChild instanceof KeyboardAwareLinearLayout)) {
- throw new IllegalStateException("cover view must be a KeyboardAwareLinearLayout");
- }
-
- coverView = (KeyboardAwareLinearLayout) coverViewChild;
+ private View getCoverView() {
+ if (coverView == null) coverView = getChildAt(coverViewPosition);
return coverView;
}
+ private KeyboardAwareLinearLayout getContainer() {
+ if (container == null) container = (KeyboardAwareLinearLayout)getParent();
+ return container;
+ }
+
private void updateHalfExpandedAnchorPoint() {
- if (getCoverView() != null) {
- slideRange = getMeasuredHeight();
- halfExpandedHeight = coverView.getKeyboardHeight();
- halfExpandedAnchorPoint = computeSlideOffsetFromCoverBottom(slideRange - halfExpandedHeight);
+ if (getContainer() != null) {
+ halfExpandedHeight = getContainer().getKeyboardHeight();
}
}
@@ -159,8 +157,6 @@ public class QuickAttachmentDrawer extends ViewGroup {
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
- updateHalfExpandedAnchorPoint();
-
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
final int childHeight = child.getMeasuredHeight();
@@ -177,8 +173,8 @@ public class QuickAttachmentDrawer extends ViewGroup {
} else if (child == controls) {
childBottom = getMeasuredHeight();
} else {
- childBottom = computeCoverBottomPosition(slideOffset);
- childTop = childBottom - childHeight;
+ childTop = computeCoverTopPosition(slideOffset);
+ childBottom = childTop + childHeight;
}
final int childRight = childLeft + child.getMeasuredWidth();
@@ -233,7 +229,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
default:
childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
break;
- }
+ }
child.measure(childWidthSpec, childHeightSpec);
}
@@ -269,11 +265,11 @@ public class QuickAttachmentDrawer extends ViewGroup {
ViewCompat.postInvalidateOnAnimation(this);
}
- if (slideOffset == COLLAPSED_ANCHOR_POINT && quickCamera.isStarted()) {
+ if (slideOffset == 0 && quickCamera.isStarted()) {
quickCamera.onPause();
controls.setVisibility(INVISIBLE);
quickCamera.setVisibility(INVISIBLE);
- } else if (slideOffset != COLLAPSED_ANCHOR_POINT && !quickCamera.isStarted() & !paused) {
+ } else if (slideOffset != 0 && !quickCamera.isStarted() & !paused) {
controls.setVisibility(VISIBLE);
quickCamera.setVisibility(VISIBLE);
quickCamera.onResume();
@@ -284,7 +280,6 @@ public class QuickAttachmentDrawer extends ViewGroup {
switch (drawerState) {
case COLLAPSED:
fullScreenButton.setImageResource(R.drawable.quick_camera_fullscreen);
- if (listener != null) listener.onAttachmentDrawerClosed();
break;
case HALF_EXPANDED:
if (isFullscreenOnly()) {
@@ -292,30 +287,37 @@ public class QuickAttachmentDrawer extends ViewGroup {
return;
}
fullScreenButton.setImageResource(R.drawable.quick_camera_fullscreen);
- if (listener != null) listener.onAttachmentDrawerOpened();
break;
case FULL_EXPANDED:
fullScreenButton.setImageResource(isFullscreenOnly() ? R.drawable.quick_camera_hide
: R.drawable.quick_camera_exit_fullscreen);
- if (listener != null) listener.onAttachmentDrawerOpened();
break;
}
- this.drawerState = drawerState;
- }
- public float getTargetSlideOffset() {
- switch (drawerState) {
- case FULL_EXPANDED: return FULL_EXPANDED_ANCHOR_POINT;
- case HALF_EXPANDED: return halfExpandedAnchorPoint;
- default: return COLLAPSED_ANCHOR_POINT;
+ if (listener != null && drawerState != this.drawerState) {
+ this.drawerState = drawerState;
+ listener.onAttachmentDrawerStateChanged(drawerState);
}
}
- public void setDrawerStateAndAnimate(final DrawerState requestedDrawerState) {
+ public int getTargetSlideOffset() {
+ switch (drawerState) {
+ case FULL_EXPANDED: return getMeasuredHeight();
+ case HALF_EXPANDED: return halfExpandedHeight;
+ default: return 0;
+ }
+ }
+
+ public void setDrawerStateAndUpdate(final DrawerState requestedDrawerState) {
+ setDrawerStateAndUpdate(requestedDrawerState, false);
+ }
+
+ public void setDrawerStateAndUpdate(final DrawerState requestedDrawerState, boolean instant) {
DrawerState oldDrawerState = this.drawerState;
setDrawerState(requestedDrawerState);
if (oldDrawerState != drawerState) {
- slideTo(getTargetSlideOffset());
+ updateHalfExpandedAnchorPoint();
+ slideTo(getTargetSlideOffset(), instant);
}
}
@@ -325,8 +327,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
}
public interface AttachmentDrawerListener extends QuickCameraListener {
- void onAttachmentDrawerClosed();
- void onAttachmentDrawerOpened();
+ void onAttachmentDrawerStateChanged(DrawerState drawerState);
}
private class ViewDragHelperCallback extends ViewDragHelper.Callback {
@@ -351,10 +352,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
- final int expandedTop = computeCoverBottomPosition(FULL_EXPANDED_ANCHOR_POINT) - coverView.getHeight();
- final int collapsedTop = computeCoverBottomPosition(COLLAPSED_ANCHOR_POINT) - coverView.getHeight();
- final int newTop = Math.min(Math.max(coverView.getTop() + dy, expandedTop), collapsedTop);
- slideOffset = computeSlideOffsetFromCoverBottom(newTop + coverView.getHeight());
+ slideOffset = Util.clamp(slideOffset - dy, 0, getMeasuredHeight());
requestLayout();
}
@@ -367,25 +365,20 @@ public class QuickAttachmentDrawer extends ViewGroup {
if (direction > 1) {
drawerState = DrawerState.FULL_EXPANDED;
} else if (direction < -1) {
- boolean halfExpand = (slideOffset > halfExpandedAnchorPoint && !isLandscape());
+ boolean halfExpand = (slideOffset > halfExpandedHeight && !isLandscape());
drawerState = halfExpand ? DrawerState.HALF_EXPANDED : DrawerState.COLLAPSED;
} else if (!isLandscape()) {
- if (halfExpandedAnchorPoint != 1 && slideOffset >= (1.f + halfExpandedAnchorPoint) / 2) {
+ if (slideOffset >= (halfExpandedHeight + getMeasuredHeight()) / 2) {
drawerState = DrawerState.FULL_EXPANDED;
- } else if (halfExpandedAnchorPoint == 1 && slideOffset >= 0.5f) {
- drawerState = DrawerState.FULL_EXPANDED;
- } else if (halfExpandedAnchorPoint != 1 && slideOffset >= halfExpandedAnchorPoint) {
- drawerState = DrawerState.HALF_EXPANDED;
- } else if (halfExpandedAnchorPoint != 1 && slideOffset >= halfExpandedAnchorPoint / 2) {
+ } else if (slideOffset >= halfExpandedHeight / 2) {
drawerState = DrawerState.HALF_EXPANDED;
}
}
setDrawerState(drawerState);
- float slideOffset = getTargetSlideOffset();
+ int slideOffset = getTargetSlideOffset();
dragHelper.captureChildView(coverView, 0);
- Log.w(TAG, String.format("setting cover at %d", computeCoverBottomPosition(slideOffset) - coverView.getHeight()));
- dragHelper.settleCapturedViewAt(coverView.getLeft(), computeCoverBottomPosition(slideOffset) - coverView.getHeight());
+ dragHelper.settleCapturedViewAt(coverView.getLeft(), computeCoverTopPosition(slideOffset));
dragHelper.captureChildView(quickCamera, 0);
dragHelper.settleCapturedViewAt(quickCamera.getLeft(), computeCameraTopPosition(slideOffset));
ViewCompat.postInvalidateOnAnimation(QuickAttachmentDrawer.this);
@@ -394,7 +387,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
@Override
public int getViewVerticalDragRange(View child) {
- return slideRange;
+ return getMeasuredHeight();
}
@Override
@@ -465,28 +458,22 @@ public class QuickAttachmentDrawer extends ViewGroup {
screenY >= viewLocation[1] && screenY < viewLocation[1] + quickCamera.getHeight();
}
- private int computeCameraTopPosition(float slideOffset) {
- float clampedOffset = slideOffset - halfExpandedAnchorPoint;
- if (clampedOffset < COLLAPSED_ANCHOR_POINT) {
- clampedOffset = COLLAPSED_ANCHOR_POINT;
- } else {
- clampedOffset = clampedOffset / (FULL_EXPANDED_ANCHOR_POINT - halfExpandedAnchorPoint);
- }
- float slidePixelOffset = slideOffset * slideRange +
- (quickCamera.getMeasuredHeight() - coverView.getKeyboardHeight()) / 2 *
- (FULL_EXPANDED_ANCHOR_POINT - clampedOffset);
- float marginPixelOffset = (getMeasuredHeight() - quickCamera.getMeasuredHeight()) / 2 * clampedOffset;
- return (int) (getMeasuredHeight() - slidePixelOffset + marginPixelOffset);
+ private int computeCameraTopPosition(int slideOffset) {
+ final int baseCameraTop = (quickCamera.getMeasuredHeight() - halfExpandedHeight) / 2;
+ final int baseOffset = getMeasuredHeight() - slideOffset - baseCameraTop;
+ final float slop = Util.clamp((float)(slideOffset - halfExpandedHeight) / (getMeasuredHeight() - halfExpandedHeight),
+ 0f,
+ 1f);
+ return baseOffset + (int)(slop * baseCameraTop);
}
- private int computeCoverBottomPosition(float slideOffset) {
- int slidePixelOffset = (int) (slideOffset * slideRange);
- return getMeasuredHeight() - getPaddingBottom() - slidePixelOffset;
+ private int computeCoverTopPosition(int slideOffset) {
+ return getMeasuredHeight() - getPaddingBottom() - slideOffset - getCoverView().getMeasuredHeight();
}
- private void slideTo(float slideOffset) {
- if (dragHelper != null && !halfModeUnsupported) {
- dragHelper.smoothSlideViewTo(coverView, coverView.getLeft(), computeCoverBottomPosition(slideOffset) - coverView.getHeight());
+ private void slideTo(int slideOffset, boolean forceInstant) {
+ if (dragHelper != null && !halfModeUnsupported && !forceInstant) {
+ dragHelper.smoothSlideViewTo(coverView, coverView.getLeft(), computeCoverTopPosition(slideOffset));
dragHelper.smoothSlideViewTo(quickCamera, quickCamera.getLeft(), computeCameraTopPosition(slideOffset));
ViewCompat.postInvalidateOnAnimation(this);
} else {
@@ -497,11 +484,6 @@ public class QuickAttachmentDrawer extends ViewGroup {
}
}
- private float computeSlideOffsetFromCoverBottom(int topPosition) {
- final int topBoundCollapsed = computeCoverBottomPosition(0);
- return (float) (topBoundCollapsed - topPosition) / slideRange;
- }
-
public void onPause() {
paused = true;
quickCamera.onPause();
@@ -524,7 +506,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
@Override
public void onClick(View v) {
boolean crop = drawerState != DrawerState.FULL_EXPANDED;
- int imageHeight = crop ? coverView.getKeyboardHeight() : quickCamera.getMeasuredHeight();
+ int imageHeight = crop ? getContainer().getKeyboardHeight() : quickCamera.getMeasuredHeight();
Rect previewRect = new Rect(0, 0, quickCamera.getMeasuredWidth(), imageHeight);
quickCamera.takePicture(previewRect);
}
@@ -543,11 +525,11 @@ public class QuickAttachmentDrawer extends ViewGroup {
@Override
public void onClick(View v) {
if (drawerState != DrawerState.FULL_EXPANDED) {
- setDrawerStateAndAnimate(DrawerState.FULL_EXPANDED);
+ setDrawerStateAndUpdate(DrawerState.FULL_EXPANDED);
} else if (isFullscreenOnly()) {
- setDrawerStateAndAnimate(DrawerState.COLLAPSED);
+ setDrawerStateAndUpdate(DrawerState.COLLAPSED);
} else {
- setDrawerStateAndAnimate(DrawerState.HALF_EXPANDED);
+ setDrawerStateAndUpdate(DrawerState.HALF_EXPANDED);
}
}
}
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
index 0ea5183618..ad5bcd1c21 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
+import android.content.res.Configuration;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
@@ -18,7 +19,7 @@ import android.widget.LinearLayout;
import com.astuetz.PagerSlidingTabStrip;
import org.thoughtcrime.securesms.R;
-import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
+import org.thoughtcrime.securesms.components.InputManager.InputView;
import org.thoughtcrime.securesms.components.RepeatableImageKey;
import org.thoughtcrime.securesms.components.RepeatableImageKey.KeyEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiPageView.EmojiSelectionListener;
@@ -27,7 +28,7 @@ import org.thoughtcrime.securesms.util.ResUtil;
import java.util.LinkedList;
import java.util.List;
-public class EmojiDrawer extends LinearLayout {
+public class EmojiDrawer extends LinearLayout implements InputView {
private static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
private ViewPager pager;
@@ -35,6 +36,7 @@ public class EmojiDrawer extends LinearLayout {
private PagerSlidingTabStrip strip;
private RecentEmojiPageModel recentModel;
private EmojiEventListener listener;
+ private EmojiDrawerListener drawerListener;
public EmojiDrawer(Context context) {
this(context, null);
@@ -43,6 +45,9 @@ public class EmojiDrawer extends LinearLayout {
public EmojiDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(VERTICAL);
+ }
+
+ private void initView() {
final View v = LayoutInflater.from(getContext()).inflate(R.layout.emoji_drawer, this, true);
initializeResources(v);
initializePageModels();
@@ -53,6 +58,10 @@ public class EmojiDrawer extends LinearLayout {
this.listener = listener;
}
+ public void setDrawerListener(EmojiDrawerListener listener) {
+ this.drawerListener = listener;
+ }
+
private void initializeResources(View v) {
Log.w("EmojiDrawer", "initializeResources()");
this.pager = (ViewPager) v.findViewById(R.id.emoji_pager);
@@ -66,20 +75,27 @@ public class EmojiDrawer extends LinearLayout {
});
}
+ @Override
public boolean isShowing() {
return getVisibility() == VISIBLE;
}
- public void show(KeyboardAwareLinearLayout container) {
+ @Override
+ public void show(int height, boolean immediate) {
+ if (this.pager == null) initView();
ViewGroup.LayoutParams params = getLayoutParams();
- params.height = container.getKeyboardHeight();
+ params.height = height;
Log.w("EmojiDrawer", "showing emoji drawer with height " + params.height);
setLayoutParams(params);
setVisibility(VISIBLE);
+ if (drawerListener != null) drawerListener.onShown();
}
- public void dismiss() {
+ @Override
+ public void hide(boolean immediate) {
setVisibility(GONE);
+ if (drawerListener != null) drawerListener.onHidden();
+ Log.w("EmojiDrawer", "hide()");
}
private void initializeEmojiGrid() {
@@ -161,4 +177,9 @@ public class EmojiDrawer extends LinearLayout {
public interface EmojiEventListener extends EmojiSelectionListener {
void onKeyEvent(KeyEvent keyEvent);
}
+
+ public interface EmojiDrawerListener {
+ void onShown();
+ void onHidden();
+ }
}
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java
index 554fd34b5c..5ffeb9b8ac 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java
@@ -8,8 +8,9 @@ import android.util.AttributeSet;
import android.widget.ImageButton;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiDrawerListener;
-public class EmojiToggle extends ImageButton {
+public class EmojiToggle extends ImageButton implements EmojiDrawerListener {
private Drawable emojiToggle;
private Drawable imeToggle;
@@ -48,4 +49,16 @@ public class EmojiToggle extends ImageButton {
drawables.recycle();
setToEmoji();
}
+
+ public void attach(EmojiDrawer drawer) {
+ drawer.setDrawerListener(this);
+ }
+
+ @Override public void onShown() {
+ setToIme();
+ }
+
+ @Override public void onHidden() {
+ setToEmoji();
+ }
}
diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java
index fda6e4bd7f..a7ce4ce806 100644
--- a/src/org/thoughtcrime/securesms/util/Util.java
+++ b/src/org/thoughtcrime/securesms/util/Util.java
@@ -348,4 +348,8 @@ public class Util {
public static int clamp(int value, int min, int max) {
return Math.min(Math.max(value, min), max);
}
+
+ public static float clamp(float value, float min, float max) {
+ return Math.min(Math.max(value, min), max);
+ }
}