diff --git a/res/drawable-hdpi/ic_photo_camera_dark.png b/res/drawable-hdpi/ic_photo_camera_dark.png
new file mode 100644
index 0000000000..f937d64f37
Binary files /dev/null and b/res/drawable-hdpi/ic_photo_camera_dark.png differ
diff --git a/res/drawable-hdpi/ic_photo_camera_light.png b/res/drawable-hdpi/ic_photo_camera_light.png
new file mode 100644
index 0000000000..f5a275c380
Binary files /dev/null and b/res/drawable-hdpi/ic_photo_camera_light.png differ
diff --git a/res/drawable-mdpi/ic_photo_camera_dark.png b/res/drawable-mdpi/ic_photo_camera_dark.png
new file mode 100644
index 0000000000..aa3a3e5717
Binary files /dev/null and b/res/drawable-mdpi/ic_photo_camera_dark.png differ
diff --git a/res/drawable-mdpi/ic_photo_camera_light.png b/res/drawable-mdpi/ic_photo_camera_light.png
new file mode 100644
index 0000000000..c26a846e37
Binary files /dev/null and b/res/drawable-mdpi/ic_photo_camera_light.png differ
diff --git a/res/drawable-xhdpi/ic_photo_camera_dark.png b/res/drawable-xhdpi/ic_photo_camera_dark.png
new file mode 100644
index 0000000000..c1a3549bfc
Binary files /dev/null and b/res/drawable-xhdpi/ic_photo_camera_dark.png differ
diff --git a/res/drawable-xhdpi/ic_photo_camera_light.png b/res/drawable-xhdpi/ic_photo_camera_light.png
new file mode 100644
index 0000000000..62d83b253d
Binary files /dev/null and b/res/drawable-xhdpi/ic_photo_camera_light.png differ
diff --git a/res/drawable-xxhdpi/ic_photo_camera_dark.png b/res/drawable-xxhdpi/ic_photo_camera_dark.png
new file mode 100644
index 0000000000..5b96a4c6b9
Binary files /dev/null and b/res/drawable-xxhdpi/ic_photo_camera_dark.png differ
diff --git a/res/drawable-xxhdpi/ic_photo_camera_light.png b/res/drawable-xxhdpi/ic_photo_camera_light.png
new file mode 100644
index 0000000000..d23e4b47a8
Binary files /dev/null and b/res/drawable-xxhdpi/ic_photo_camera_light.png differ
diff --git a/res/drawable-xxxhdpi/ic_photo_camera_dark.png b/res/drawable-xxxhdpi/ic_photo_camera_dark.png
new file mode 100644
index 0000000000..23a9c2efd0
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_photo_camera_dark.png differ
diff --git a/res/drawable-xxxhdpi/ic_photo_camera_light.png b/res/drawable-xxxhdpi/ic_photo_camera_light.png
new file mode 100644
index 0000000000..7f43cc3152
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_photo_camera_light.png differ
diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index 584d735204..897560ed06 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -97,19 +97,37 @@
android:contentDescription="@string/conversation_activity__compose_description"
android:textColor="?conversation_editor_text_color" />
-
+
+
+
+
+
+
+
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 4676554e84..0d81e30bc2 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -37,6 +37,7 @@
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e03e974f0e..098e948456 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -50,6 +50,7 @@
Can\'t find an app to select media.
+ Take Photo
Picture
Video
Audio
diff --git a/res/values/themes.xml b/res/values/themes.xml
index b2af44d0ff..aa6eb50b24 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -53,6 +53,7 @@
- @drawable/ic_movie_creation_light
- @drawable/ic_volume_up_light
- @drawable/ic_account_box_light
+ - @drawable/ic_photo_camera_light
- @drawable/conversation_item_background
- @color/conversation_item_received_background_light
@@ -188,6 +189,7 @@
- @drawable/ic_movie_creation_dark
- @drawable/ic_volume_up_dark
- @drawable/ic_account_box_dark
+ - @drawable/ic_photo_camera_dark
- @drawable/ic_add_white_24dp
- @drawable/ic_group_white_24dp
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index e4999e15e3..11ddd2618e 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -24,7 +24,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
@@ -44,6 +43,7 @@ import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
@@ -51,9 +51,9 @@ import com.afollestad.materialdialogs.AlertDialogWrapper;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
+import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.components.ComposeText;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
-import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
@@ -136,10 +136,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int PICK_AUDIO = 3;
private static final int PICK_CONTACT_INFO = 4;
private static final int GROUP_EDIT = 5;
+ private static final int CAPTURE_PHOTO = 6;
private MasterSecret masterSecret;
private ComposeText composeText;
+ private AnimatingToggle buttonToggle;
private SendButton sendButton;
+ private ImageButton attachButton;
private TextView charactersLeft;
private ConversationFragment fragment;
@@ -237,7 +240,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.w(TAG, "onActivityResult called: " + reqCode + ", " + resultCode + " , " + data);
super.onActivityResult(reqCode, resultCode, data);
- if (data == null || resultCode != RESULT_OK) return;
+ if ((data == null && reqCode != CAPTURE_PHOTO) || resultCode != RESULT_OK) return;
+
switch (reqCode) {
case PICK_IMAGE:
addAttachmentImage(data.getData());
@@ -251,6 +255,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case PICK_CONTACT_INFO:
addAttachmentContactInfo(data.getData());
break;
+ case CAPTURE_PHOTO:
+ if (attachmentManager.getCaptureFile() != null) {
+ addAttachmentImage(Uri.fromFile(attachmentManager.getCaptureFile()));
+ }
+ break;
case GROUP_EDIT:
this.recipients = RecipientFactory.getRecipientsForIds(this, data.getLongArrayExtra(GroupCreateActivity.GROUP_RECIPIENT_EXTRA), true);
initializeTitleBar();
@@ -596,6 +605,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (draftText == null && draftImage == null && draftAudio == null && draftVideo == null) {
initializeDraftFromDatabase();
+ } else {
+ updateToggleButtonState();
}
}
@@ -631,6 +642,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
addAttachmentVideo(Uri.parse(draft.getValue()));
}
}
+
+ updateToggleButtonState();
}
}.execute();
}
@@ -675,7 +688,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void initializeViews() {
+ buttonToggle = (AnimatingToggle)findViewById(R.id.button_toggle);
sendButton = (SendButton) findViewById(R.id.send_button);
+ attachButton = (ImageButton) findViewById(R.id.attach_button);
composeText = (ComposeText) findViewById(R.id.embedded_text_editor);
charactersLeft = (TextView) findViewById(R.id.space_left);
emojiDrawer = Optional.absent();
@@ -687,6 +702,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
SendButtonListener sendButtonListener = new SendButtonListener();
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
+ attachButton.setOnClickListener(new AttachButtonListener());
sendButton.setOnClickListener(sendButtonListener);
sendButton.setEnabled(true);
sendButton.addOnTransportChangedListener(new OnTransportChangedListener() {
@@ -775,6 +791,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void addAttachment(int type) {
Log.w("ComposeMessageActivity", "Selected: " + type);
switch (type) {
+ case AttachmentTypeSelectorAdapter.TAKE_PHOTO:
+ attachmentManager.capturePhoto(this, CAPTURE_PHOTO); break;
case AttachmentTypeSelectorAdapter.ADD_IMAGE:
AttachmentManager.selectImage(this, PICK_IMAGE); break;
case AttachmentTypeSelectorAdapter.ADD_VIDEO:
@@ -989,6 +1007,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
fragment.scrollToBottom();
+ attachmentManager.cleanup();
}
private void sendMessage() {
@@ -1081,6 +1100,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}.execute(message);
}
+ private void updateToggleButtonState() {
+ if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
+ buttonToggle.display(attachButton);
+ } else {
+ buttonToggle.display(sendButton);
+ }
+ }
// Listeners
@@ -1124,7 +1150,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
+ private class AttachButtonListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ handleAddAttachment();
+ }
+ }
+
private class ComposeKeyPressedListener implements OnKeyListener, OnClickListener, TextWatcher, OnFocusChangeListener {
+
+ int beforeLength;
+
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -1146,12 +1182,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,int after) {
+ beforeLength = composeText.getText().length();
+ }
+
@Override
public void afterTextChanged(Editable s) {
calculateCharactersRemaining();
+
+ if (composeText.getText().length() == 0 || beforeLength == 0) {
+ updateToggleButtonState();
+ }
}
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,int after) {}
+
@Override
public void onTextChanged(CharSequence s, int start, int before,int count) {}
@@ -1176,5 +1220,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onAttachmentChanged() {
initializeSecurity();
+ updateToggleButtonState();
}
}
diff --git a/src/org/thoughtcrime/securesms/components/AnimatingToggle.java b/src/org/thoughtcrime/securesms/components/AnimatingToggle.java
new file mode 100644
index 0000000000..e88808ef8d
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/AnimatingToggle.java
@@ -0,0 +1,108 @@
+package org.thoughtcrime.securesms.components;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.widget.FrameLayout;
+
+public class AnimatingToggle extends FrameLayout {
+
+ private static final int SPEED_MILLIS = 200;
+
+ public AnimatingToggle(Context context) {
+ super(context);
+ }
+
+ public AnimatingToggle(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AnimatingToggle(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
+ super.addView(child, index, params);
+
+ if (getChildCount() == 1) child.setVisibility(View.VISIBLE);
+ else child.setVisibility(View.GONE);
+ }
+
+ public void display(View view) {
+ if (view.getVisibility() == View.VISIBLE) return;
+
+ int oldViewIndex = getVisibleViewIndex();
+ int newViewIndex = getViewIndex(view);
+
+ int sign;
+
+ if (oldViewIndex < newViewIndex) sign = -1;
+ else sign = 1;
+
+ TranslateAnimation oldViewAnimation = createTranslation(0.0f, sign * 1.0f);
+ TranslateAnimation newViewAnimation = createTranslation(sign * -1.0f, 0.0f);
+
+ animateOut(oldViewIndex, oldViewAnimation);
+ animateIn(newViewIndex, newViewAnimation);
+ }
+
+ private void animateOut(int viewIndex, TranslateAnimation animation) {
+ final View view = getChildAt(viewIndex);
+
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ view.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+
+ view.startAnimation(animation);
+ }
+
+ private void animateIn(int viewIndex, TranslateAnimation animation) {
+ final View view = getChildAt(viewIndex);
+ view.setVisibility(View.VISIBLE);
+ view.startAnimation(animation);
+ }
+
+ private int getVisibleViewIndex() {
+ for (int i=0;i getItemList(Context context) {
List data = new ArrayList<>(4);
- addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_picture), ResUtil.getDrawableRes(context, R.attr.conversation_attach_image), ADD_IMAGE);
- addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_video), ResUtil.getDrawableRes(context, R.attr.conversation_attach_video), ADD_VIDEO);
- addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_audio), ResUtil.getDrawableRes(context, R.attr.conversation_attach_sound), ADD_SOUND);
- addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_contact), ResUtil.getDrawableRes(context, R.attr.conversation_attach_contact_info), ADD_CONTACT_INFO);
+ addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_take_photo), ResUtil.getDrawableRes(context, R.attr.conversation_attach_photo), TAKE_PHOTO);
+ addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_picture), ResUtil.getDrawableRes(context, R.attr.conversation_attach_image), ADD_IMAGE);
+ addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_video), ResUtil.getDrawableRes(context, R.attr.conversation_attach_video), ADD_VIDEO);
+ addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_audio), ResUtil.getDrawableRes(context, R.attr.conversation_attach_sound), ADD_SOUND);
+ addItem(data, context.getString(R.string.AttachmentTypeSelectorAdapter_contact), ResUtil.getDrawableRes(context, R.attr.conversation_attach_contact_info), ADD_CONTACT_INFO);
return data;
}