diff --git a/res/anim/animation_toggle_in.xml b/res/anim/animation_toggle_in.xml new file mode 100644 index 0000000000..ffffd02d1e --- /dev/null +++ b/res/anim/animation_toggle_in.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/res/anim/animation_toggle_out.xml b/res/anim/animation_toggle_out.xml new file mode 100644 index 0000000000..1e6f7ed638 --- /dev/null +++ b/res/anim/animation_toggle_out.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/res/drawable-hdpi/ic_send_push_white_24dp.png b/res/drawable-hdpi/ic_send_push_white_24dp.png new file mode 100644 index 0000000000..64f6ceb372 Binary files /dev/null and b/res/drawable-hdpi/ic_send_push_white_24dp.png differ diff --git a/res/drawable-hdpi/ic_send_sms_white_24dp.png b/res/drawable-hdpi/ic_send_sms_white_24dp.png new file mode 100644 index 0000000000..129a0235c5 Binary files /dev/null and b/res/drawable-hdpi/ic_send_sms_white_24dp.png differ diff --git a/res/drawable-mdpi/ic_send_push_white_24dp.png b/res/drawable-mdpi/ic_send_push_white_24dp.png new file mode 100644 index 0000000000..7d1e725b40 Binary files /dev/null and b/res/drawable-mdpi/ic_send_push_white_24dp.png differ diff --git a/res/drawable-mdpi/ic_send_sms_white_24dp.png b/res/drawable-mdpi/ic_send_sms_white_24dp.png new file mode 100644 index 0000000000..74ac8833e7 Binary files /dev/null and b/res/drawable-mdpi/ic_send_sms_white_24dp.png differ diff --git a/res/drawable-v21/circle_touch_highlight_background.xml b/res/drawable-v21/circle_touch_highlight_background.xml new file mode 100644 index 0000000000..fe392b45e6 --- /dev/null +++ b/res/drawable-v21/circle_touch_highlight_background.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/res/drawable-xhdpi/ic_send_push_white_24dp.png b/res/drawable-xhdpi/ic_send_push_white_24dp.png new file mode 100644 index 0000000000..2a0f54fb53 Binary files /dev/null and b/res/drawable-xhdpi/ic_send_push_white_24dp.png differ diff --git a/res/drawable-xhdpi/ic_send_sms_white_24dp.png b/res/drawable-xhdpi/ic_send_sms_white_24dp.png new file mode 100644 index 0000000000..c8680adb2a Binary files /dev/null and b/res/drawable-xhdpi/ic_send_sms_white_24dp.png differ diff --git a/res/drawable-xxhdpi/ic_send_push_white_24dp.png b/res/drawable-xxhdpi/ic_send_push_white_24dp.png new file mode 100644 index 0000000000..3935e2851d Binary files /dev/null and b/res/drawable-xxhdpi/ic_send_push_white_24dp.png differ diff --git a/res/drawable-xxhdpi/ic_send_sms_white_24dp.png b/res/drawable-xxhdpi/ic_send_sms_white_24dp.png new file mode 100644 index 0000000000..c0d1416c94 Binary files /dev/null and b/res/drawable-xxhdpi/ic_send_sms_white_24dp.png differ diff --git a/res/drawable-xxhdpi/ic_send_white_24dp.png b/res/drawable-xxhdpi/ic_send_white_24dp.png new file mode 100644 index 0000000000..cbb64e09f4 Binary files /dev/null and b/res/drawable-xxhdpi/ic_send_white_24dp.png differ diff --git a/res/drawable-xxxhdpi/ic_send_push_white_24dp.png b/res/drawable-xxxhdpi/ic_send_push_white_24dp.png new file mode 100644 index 0000000000..79211258f5 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_send_push_white_24dp.png differ diff --git a/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png b/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png new file mode 100644 index 0000000000..0976f95f75 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png differ diff --git a/res/drawable/circle_tintable.xml b/res/drawable/circle_tintable.xml new file mode 100644 index 0000000000..6c5c360635 --- /dev/null +++ b/res/drawable/circle_tintable.xml @@ -0,0 +1,5 @@ + + + + diff --git a/res/drawable/circle_touch_highlight_background.xml b/res/drawable/circle_touch_highlight_background.xml new file mode 100644 index 0000000000..96f6b341a6 --- /dev/null +++ b/res/drawable/circle_touch_highlight_background.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/res/drawable/compose_background.xml b/res/drawable/compose_background.xml index c3369ec70f..6e74c4b6fa 100644 --- a/res/drawable/compose_background.xml +++ b/res/drawable/compose_background.xml @@ -1,9 +1,5 @@ - - + \ No newline at end of file diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml index 5c7bdf651c..99be48928d 100644 --- a/res/layout/conversation_activity.xml +++ b/res/layout/conversation_activity.xml @@ -4,6 +4,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="fill_parent" + xmlns:tools="http://schemas.android.com/tools" android:background="?conversation_background" android:orientation="vertical"> @@ -60,73 +61,65 @@ - + - + - + android:contentDescription="@string/conversation_activity__emoji_toggle_description" /> - + tools:hint="Send TextSecure message" /> + + android:id="@+id/button_toggle" + android:layout_width="50dp" + android:layout_height="50dp" + android:background="@drawable/circle_tintable" + android:layout_gravity="bottom"> + android:id="@+id/attach_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:background="@drawable/circle_touch_highlight_background" + android:src="@drawable/ic_attach_white_24dp" + android:contentDescription="@string/ConversationActivity_add_attachment" + android:nextFocusLeft="@+id/embedded_text_editor" /> + android:id="@+id/send_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/conversation_activity__send" + android:nextFocusLeft="@+id/embedded_text_editor" + android:src="?conversation_transport_sms_indicator" + android:background="@drawable/circle_touch_highlight_background" /> diff --git a/res/layout/transport_selection.xml b/res/layout/transport_selection.xml index 39951ca18d..d532f27d91 100644 --- a/res/layout/transport_selection.xml +++ b/res/layout/transport_selection.xml @@ -4,7 +4,8 @@ android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="?conversation_transport_popup_background"> + android:background="?conversation_transport_popup_background" + android:elevation="2dp"> diff --git a/res/layout/transport_selection_list_item.xml b/res/layout/transport_selection_list_item.xml index ba3ce5b8d7..8a6033e815 100644 --- a/res/layout/transport_selection_list_item.xml +++ b/res/layout/transport_selection_list_item.xml @@ -1,6 +1,7 @@ + android:padding="7dp" + android:background="@drawable/circle_tintable" + android:contentDescription="@string/transport_selection_list_item__transport_icon" + tools:src="@drawable/ic_send_push_white_24dp" + tools:backgroundTint="@color/textsecure_primary" /> - \ No newline at end of file + android:layout_height="wrap_content" + tools:text="TextSecure" /> + diff --git a/res/values-v19/colors.xml b/res/values-v19/colors.xml index 4d2b6f2d81..8eee1017ab 100644 --- a/res/values-v19/colors.xml +++ b/res/values-v19/colors.xml @@ -1,4 +1,4 @@ - #10000000 + #22000000 \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index ca2e62ef97..d4b87973bd 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -7,8 +7,8 @@ 5dp 12sp 200sp - 0dp - 1dp + 2dp + 2dp 64dp 50dp 230dp diff --git a/res/values/styles.xml b/res/values/styles.xml index 72e11def65..94f39187a4 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -176,4 +176,18 @@ @drawable/error_round + + diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 2354713293..f76a2f57dc 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -23,6 +23,11 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.AsyncTask; @@ -30,6 +35,7 @@ import android.os.Build; import android.os.Bundle; import android.provider.ContactsContract; import android.support.annotation.NonNull; +import android.support.v4.graphics.drawable.DrawableCompat; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; @@ -154,6 +160,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private ConversationFragment fragment; private Button unblockButton; private View composePanel; + private View composeBubble; private AttachmentTypeSelectorAdapter attachmentAdapter; private AttachmentManager attachmentManager; @@ -732,6 +739,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity titleView = (ConversationTitleView) getSupportActionBar().getCustomView(); unblockButton = (Button) findViewById(R.id.unblock_button); composePanel = findViewById(R.id.bottom_panel); + composeBubble = findViewById(R.id.compose_bubble); + + int[] attributes = new int[]{R.attr.conversation_item_bubble_background}; + TypedArray colors = obtainStyledAttributes(attributes); + int defaultColor = colors.getColor(0, Color.WHITE); + composeBubble.getBackground().setColorFilter(defaultColor, PorterDuff.Mode.MULTIPLY); + colors.recycle(); attachmentAdapter = new AttachmentTypeSelectorAdapter(this); attachmentManager = new AttachmentManager(this, this); @@ -747,6 +761,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public void onChange(TransportOption newTransport) { calculateCharactersRemaining(); composeText.setHint(newTransport.getComposeHint()); + buttonToggle.getBackground().setColorFilter(newTransport.getBackgroundColor(), Mode.MULTIPLY); } }); diff --git a/src/org/thoughtcrime/securesms/TransportOption.java b/src/org/thoughtcrime/securesms/TransportOption.java index 15b117ef55..875f1114ad 100644 --- a/src/org/thoughtcrime/securesms/TransportOption.java +++ b/src/org/thoughtcrime/securesms/TransportOption.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms; +import android.support.annotation.DrawableRes; + import org.thoughtcrime.securesms.util.CharacterCalculator; import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; @@ -11,19 +13,22 @@ public class TransportOption { } private int drawable; + private int backgroundColor; private String text; private Type type; private String composeHint; private CharacterCalculator characterCalculator; public TransportOption(Type type, - int drawable, + @DrawableRes int drawable, + int backgroundColor, String text, String composeHint, CharacterCalculator characterCalculator) { this.type = type; this.drawable = drawable; + this.backgroundColor = backgroundColor; this.text = text; this.composeHint = composeHint; this.characterCalculator = characterCalculator; @@ -37,10 +42,6 @@ public class TransportOption { return this.type == type; } - public boolean isPlaintext() { - return type == Type.SMS; - } - public boolean isSms() { return type == Type.SMS; } @@ -49,10 +50,14 @@ public class TransportOption { return characterCalculator.calculateCharacters(charactersSpent); } - public int getDrawable() { + public @DrawableRes int getDrawable() { return drawable; } + public int getBackgroundColor() { + return backgroundColor; + } + public String getComposeHint() { return composeHint; } diff --git a/src/org/thoughtcrime/securesms/TransportOptions.java b/src/org/thoughtcrime/securesms/TransportOptions.java index fd9b6ab950..81854a0443 100644 --- a/src/org/thoughtcrime/securesms/TransportOptions.java +++ b/src/org/thoughtcrime/securesms/TransportOptions.java @@ -82,32 +82,28 @@ public class TransportOptions { } private List initializeAvailableTransports(boolean isMediaMessage) { - List results = new LinkedList<>(); - int[] attributes = new int[]{R.attr.conversation_transport_sms_indicator, - R.attr.conversation_transport_push_indicator}; - TypedArray iconArray = context.obtainStyledAttributes(attributes); - int smsIconResource = iconArray.getResourceId(0, -1); - int pushIconResource = iconArray.getResourceId(1, -1); + List results = new LinkedList<>(); if (isMediaMessage) { - results.add(new TransportOption(Type.SMS, smsIconResource, + results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp, + context.getResources().getColor(R.color.grey_600), context.getString(R.string.ConversationActivity_transport_insecure_mms), context.getString(R.string.conversation_activity__type_message_mms_insecure), new MmsCharacterCalculator())); } else { - results.add(new TransportOption(Type.SMS, smsIconResource, + results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp, + context.getResources().getColor(R.color.grey_600), context.getString(R.string.ConversationActivity_transport_insecure_sms), context.getString(R.string.conversation_activity__type_message_sms_insecure), new SmsCharacterCalculator())); } - results.add(new TransportOption(Type.TEXTSECURE, pushIconResource, + results.add(new TransportOption(Type.TEXTSECURE, R.drawable.ic_send_push_white_24dp, + context.getResources().getColor(R.color.textsecure_primary), context.getString(R.string.ConversationActivity_transport_textsecure), context.getString(R.string.conversation_activity__type_message_push), new PushCharacterCalculator())); - iconArray.recycle(); - return results; } diff --git a/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java b/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java index 33b718d09a..72d10a1f28 100644 --- a/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java +++ b/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms; import android.content.Context; +import android.graphics.PorterDuff.Mode; import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; @@ -54,6 +55,7 @@ public class TransportOptionsAdapter extends BaseAdapter { ImageView imageView = (ImageView) convertView.findViewById(R.id.icon); TextView textView = (TextView) convertView.findViewById(R.id.text); + imageView.getBackground().setColorFilter(transport.getBackgroundColor(), Mode.MULTIPLY); imageView.setImageResource(transport.getDrawable()); textView.setText(transport.getDescription()); diff --git a/src/org/thoughtcrime/securesms/TransportOptionsPopup.java b/src/org/thoughtcrime/securesms/TransportOptionsPopup.java index 3cbaf99f99..f813d778ae 100644 --- a/src/org/thoughtcrime/securesms/TransportOptionsPopup.java +++ b/src/org/thoughtcrime/securesms/TransportOptionsPopup.java @@ -1,11 +1,17 @@ package org.thoughtcrime.securesms; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.drawable.BitmapDrawable; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewTreeObserver; +import android.view.ViewAnimationUtils; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ListView; @@ -20,6 +26,9 @@ public class TransportOptionsPopup implements ListView.OnItemClickListener { private final PopupWindow popupWindow; private final SelectedListener listener; + private OnGlobalLayoutListener observer; + private View parent; + public TransportOptionsPopup(@NonNull Context context, @NonNull SelectedListener listener) { this.listener = listener; this.adapter = new TransportOptionsAdapter(context, new LinkedList()); @@ -27,7 +36,6 @@ public class TransportOptionsPopup implements ListView.OnItemClickListener { View selectionMenu = LayoutInflater.from(context).inflate(R.layout.transport_selection, null); ListView listView = (ListView) selectionMenu.findViewById(R.id.transport_selection_list); - listView.setAdapter(adapter); this.popupWindow = new PopupWindow(selectionMenu); @@ -36,6 +44,10 @@ public class TransportOptionsPopup implements ListView.OnItemClickListener { this.popupWindow.setOutsideTouchable(true); this.popupWindow.setWindowLayoutMode(0, WindowManager.LayoutParams.WRAP_CONTENT); this.popupWindow.setWidth(context.getResources().getDimensionPixelSize(R.dimen.transport_selection_popup_width)); + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + this.popupWindow.setAnimationStyle(0); + this.popupWindow.setElevation(context.getResources().getDimensionPixelSize(R.dimen.transport_selection_popup_yoff)); + } listView.setOnItemClickListener(this); } @@ -48,17 +60,65 @@ public class TransportOptionsPopup implements ListView.OnItemClickListener { final int yoff = context.getResources().getDimensionPixelOffset(R.dimen.transport_selection_popup_yoff); popupWindow.showAsDropDown(parent, xoff, yoff); + animateInIfAvailable(); - parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + this.parent = parent; + this.observer = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { popupWindow.update(parent, xoff, yoff, -1, -1); } - }); + }; + parent.getViewTreeObserver().addOnGlobalLayoutListener(observer); + } + + @TargetApi(VERSION_CODES.LOLLIPOP) private Animator getCircularReveal(View v, boolean in) { + int outBound = Math.max(v.getWidth(), v.getHeight()); + return ViewAnimationUtils.createCircularReveal(v, + v.getMeasuredWidth(), + v.getMeasuredHeight(), + in ? 0 : outBound, + in ? outBound : 0) + .setDuration(200); + } + + private void animateInIfAvailable() { + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + popupWindow.getContentView().getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { + @Override @TargetApi(VERSION_CODES.LOLLIPOP) public void onGlobalLayout() { + parent.getViewTreeObserver().removeGlobalOnLayoutListener(this); + if (popupWindow.getContentView().isAttachedToWindow()) { + getCircularReveal(popupWindow.getContentView(), true).start(); + } + } + }); + } + } + + private void animateOutIfAvailable() { + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + Animator animator = getCircularReveal(popupWindow.getContentView(), false); + animator.addListener(new AnimatorListener() { + @Override public void onAnimationStart(Animator animation) {} + @Override public void onAnimationCancel(Animator animation) {} + @Override public void onAnimationRepeat(Animator animation) {} + @Override public void onAnimationEnd(Animator animation) { + popupWindow.dismiss(); + } + }); + animator.start(); + } else { + popupWindow.dismiss(); + } } public void dismiss() { - this.popupWindow.dismiss(); + animateOutIfAvailable(); + if (this.observer != null && this.parent != null) { + parent.getViewTreeObserver().removeGlobalOnLayoutListener(observer); + } + this.observer = null; + this.parent = null; } @Override @@ -67,7 +127,7 @@ public class TransportOptionsPopup implements ListView.OnItemClickListener { } public interface SelectedListener { - public void onSelected(TransportOption option); + void onSelected(TransportOption option); } } diff --git a/src/org/thoughtcrime/securesms/components/AnimatingToggle.java b/src/org/thoughtcrime/securesms/components/AnimatingToggle.java index ea43faa971..01f45faebe 100644 --- a/src/org/thoughtcrime/securesms/components/AnimatingToggle.java +++ b/src/org/thoughtcrime/securesms/components/AnimatingToggle.java @@ -7,9 +7,13 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.AnimationUtils; import android.view.animation.TranslateAnimation; import android.widget.FrameLayout; +import org.thoughtcrime.securesms.R; + public class AnimatingToggle extends FrameLayout { private static final int SPEED_MILLIS = 200; @@ -38,29 +42,19 @@ public class AnimatingToggle extends FrameLayout { } else { child.setVisibility(View.GONE); } + child.setClickable(false); } public void display(View view) { if (view == current) return; - int oldViewIndex = getViewIndex(current); - 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(current, oldViewAnimation); - animateIn(view, newViewAnimation); + animateOut(current, AnimationUtils.loadAnimation(getContext(), R.anim.animation_toggle_out)); + animateIn(view, AnimationUtils.loadAnimation(getContext(), R.anim.animation_toggle_in)); current = view; } - private void animateOut(final View view, TranslateAnimation animation) { + private void animateOut(final View view, Animation animation) { animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { @@ -79,7 +73,7 @@ public class AnimatingToggle extends FrameLayout { view.startAnimation(animation); } - private void animateIn(View view, TranslateAnimation animation) { + private void animateIn(View view, Animation animation) { animation.setInterpolator(new FastOutSlowInInterpolator()); view.setVisibility(View.VISIBLE); view.startAnimation(animation); @@ -92,17 +86,4 @@ public class AnimatingToggle extends FrameLayout { throw new IllegalArgumentException("Not a parent of this view."); } - - private TranslateAnimation createTranslation(float startY, float endY) { - TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, - Animation.RELATIVE_TO_SELF, 0.0f, - Animation.RELATIVE_TO_SELF, startY, - Animation.RELATIVE_TO_SELF, endY); - - translateAnimation.setDuration(SPEED_MILLIS); - translateAnimation.setInterpolator(new FastOutSlowInInterpolator()); - - return translateAnimation; - } - }