new composition styling

Closes #3506
// FREEBIE
This commit is contained in:
Jake McGinty 2015-06-30 17:45:39 -07:00 committed by Moxie Marlinspike
parent 140cbe9f25
commit daa98107c3
29 changed files with 234 additions and 113 deletions

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="150"
android:fromXScale="0.6"
android:fromYScale="0.6"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%" />
<alpha
android:duration="150"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="150"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.60"
android:toYScale="0.60"
android:pivotX="50%"
android:pivotY="50%" />
<alpha
android:duration="150"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/touch_highlight">
<item android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="@color/black" />
</shape>
</item>
</ripple>

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/white" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval">
<solid android:color="@color/touch_highlight" />
</shape>
</item>
</selector>

View File

@ -1,9 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<padding android:bottom="@dimen/conversation_activity_compose_padding"
android:left="@dimen/conversation_activity_compose_padding"
android:right="@dimen/conversation_activity_compose_padding"
android:top="@dimen/conversation_activity_compose_padding" />
<solid android:color="@null" />
</shape>

View File

@ -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 @@
</FrameLayout>
</ScrollView>
<View android:layout_width="match_parent"
android:layout_height="1px"
android:background="?conversation_editor_background" />
<LinearLayout android:id="@+id/bottom_panel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="5dp">
<LinearLayout
android:id="@+id/bottom_panel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout android:id="@+id/compose_bubble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/sent_bubble">
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
android:id="@+id/emoji_toggle"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_width="37dp"
android:layout_height="37dp"
android:layout_gravity="bottom"
android:background="@drawable/touch_highlight_background"
android:contentDescription="@string/conversation_activity__emoji_toggle_description"
android:padding="10dp"/>
android:contentDescription="@string/conversation_activity__emoji_toggle_description" />
<org.thoughtcrime.securesms.components.ComposeText
<org.thoughtcrime.securesms.components.ComposeText
style="@style/ComposeEditText"
android:id="@+id/embedded_text_editor"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:autoText="true"
android:capitalize="sentences"
android:background="@drawable/compose_background"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:paddingRight="0dp"
android:paddingLeft="0dp"
android:imeOptions="actionSend|flagNoEnterAction|flagNoExtractUi"
android:inputType="textAutoCorrect|textCapSentences|textMultiLine"
android:maxLength="2000"
android:maxLines="4"
android:minHeight="37dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:nextFocusForward="@+id/send_button"
android:nextFocusRight="@+id/send_button"
android:contentDescription="@string/conversation_activity__compose_description"
android:textColor="?conversation_editor_text_color" />
tools:hint="Send TextSecure message" />
</LinearLayout>
<org.thoughtcrime.securesms.components.AnimatingToggle
android:id="@+id/button_toggle"
android:layout_width="46dp"
android:layout_height="44dp"
android:layout_gravity="bottom">
android:id="@+id/button_toggle"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/circle_tintable"
android:layout_gravity="bottom">
<ImageButton
android:id="@+id/attach_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/touch_highlight_background"
android:src="?conversation_attach"
android:contentDescription="@string/ConversationActivity_add_attachment"
android:nextFocusLeft="@+id/embedded_text_editor" />
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" />
<org.thoughtcrime.securesms.components.SendButton
android:id="@+id/send_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/touch_highlight_background"
android:contentDescription="@string/conversation_activity__send"
android:nextFocusLeft="@+id/embedded_text_editor"
android:src="?conversation_transport_sms_indicator"
android:focusable="true"
android:clickable="false"
android:enabled="false" />
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" />
</org.thoughtcrime.securesms.components.AnimatingToggle>

View File

@ -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">
<ListView android:id="@+id/transport_selection_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="@dimen/transport_selection_popup_width"
android:layout_height="60dp"
@ -10,11 +11,16 @@
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/transport_selection_list_item__transport_icon"/>
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" />
<TextView android:id="@+id/text"
android:paddingLeft="10dp"
android:fontFamily="sans-serif-light"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
android:layout_height="wrap_content"
tools:text="TextSecure" />
</LinearLayout>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="touch_highlight">#10000000</color>
<color name="touch_highlight">#22000000</color>
</resources>

View File

@ -7,8 +7,8 @@
<dimen name="emoji_drawer_left_right_padding">5dp</dimen>
<dimen name="conversation_item_date_text_size">12sp</dimen>
<dimen name="transport_selection_popup_width">200sp</dimen>
<dimen name="transport_selection_popup_xoff">0dp</dimen>
<dimen name="transport_selection_popup_yoff">1dp</dimen>
<dimen name="transport_selection_popup_xoff">2dp</dimen>
<dimen name="transport_selection_popup_yoff">2dp</dimen>
<dimen name="contact_photo_target_size">64dp</dimen>
<dimen name="contact_selection_photo_size">50dp</dimen>
<dimen name="thumbnail_max_size">230dp</dimen>

View File

@ -176,4 +176,18 @@
<item name="android:background">@drawable/error_round</item>
</style>
<style name="ComposeEditText" parent="@style/Widget.AppCompat.EditText">
<item name="android:padding">2dp</item>
<item name="android:background">@null</item>
<item name="android:maxLines">4</item>
<item name="android:maxLength">2000</item>
<item name="android:textColor">?conversation_item_sent_text_primary_color</item>
<item name="android:capitalize">sentences</item>
<item name="android:autoText">true</item>
<item name="android:gravity">center_vertical</item>
<item name="android:imeOptions">actionSend|flagNoEnterAction|flagNoExtractUi</item>
<item name="android:inputType">textAutoCorrect|textCapSentences|textMultiLine</item>
<item name="android:contentDescription">@string/conversation_activity__compose_description</item>
</style>
</resources>

View File

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

View File

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

View File

@ -82,32 +82,28 @@ public class TransportOptions {
}
private List<TransportOption> initializeAvailableTransports(boolean isMediaMessage) {
List<TransportOption> 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<TransportOption> 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;
}

View File

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

View File

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

View File

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