diff --git a/res/drawable-hdpi/transfer_controls_background.9.png b/res/drawable-hdpi/transfer_controls_background.9.png
new file mode 100644
index 0000000000..0e503fb512
Binary files /dev/null and b/res/drawable-hdpi/transfer_controls_background.9.png differ
diff --git a/res/drawable-mdpi/transfer_controls_background.9.png b/res/drawable-mdpi/transfer_controls_background.9.png
new file mode 100644
index 0000000000..3e63204968
Binary files /dev/null and b/res/drawable-mdpi/transfer_controls_background.9.png differ
diff --git a/res/drawable-xhdpi/transfer_controls_background.9.png b/res/drawable-xhdpi/transfer_controls_background.9.png
new file mode 100644
index 0000000000..8f4d75a97b
Binary files /dev/null and b/res/drawable-xhdpi/transfer_controls_background.9.png differ
diff --git a/res/drawable-xxhdpi/transfer_controls_background.9.png b/res/drawable-xxhdpi/transfer_controls_background.9.png
new file mode 100644
index 0000000000..cdbc966d88
Binary files /dev/null and b/res/drawable-xxhdpi/transfer_controls_background.9.png differ
diff --git a/res/drawable-xxxhdpi/transfer_controls_background.9.png b/res/drawable-xxxhdpi/transfer_controls_background.9.png
new file mode 100644
index 0000000000..4aad138cfd
Binary files /dev/null and b/res/drawable-xxxhdpi/transfer_controls_background.9.png differ
diff --git a/res/layout/transfer_controls_view.xml b/res/layout/transfer_controls_view.xml
index 2a8888929f..f696f9d681 100644
--- a/res/layout/transfer_controls_view.xml
+++ b/res/layout/transfer_controls_view.xml
@@ -4,19 +4,22 @@
-
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a4666889bf..481601a441 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -34,4 +34,7 @@
250dp
52dp
+
+ 150dp
+ 70dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c321f70f97..dac80e337b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -384,6 +384,11 @@
Registration error
TextSecure registration has encountered a problem.
+
+ Image
+ Audio
+ Video
+
Received corrupted key
exchange message!
diff --git a/src/org/thoughtcrime/securesms/components/AnimatingToggle.java b/src/org/thoughtcrime/securesms/components/AnimatingToggle.java
index 03a13f73b8..30b100b89a 100644
--- a/src/org/thoughtcrime/securesms/components/AnimatingToggle.java
+++ b/src/org/thoughtcrime/securesms/components/AnimatingToggle.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -12,21 +11,27 @@ import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.util.ViewUtil;
public class AnimatingToggle extends FrameLayout {
private View current;
+ private final Animation inAnimation;
+ private final Animation outAnimation;
+
public AnimatingToggle(Context context) {
- super(context);
+ this(context, null);
}
public AnimatingToggle(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public AnimatingToggle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ this.outAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.animation_toggle_out);
+ this.inAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.animation_toggle_in);
}
@Override
@@ -43,45 +48,10 @@ public class AnimatingToggle extends FrameLayout {
}
public void display(@Nullable View view) {
- display(view, true);
- }
-
- protected void display(@Nullable View view, boolean animated) {
if (view == current) return;
-
- if (animated) {
- if (current != null) animateOut(current, AnimationUtils.loadAnimation(getContext(), R.anim.animation_toggle_out));
- if (view != null) animateIn(view, AnimationUtils.loadAnimation(getContext(), R.anim.animation_toggle_in));
- } else {
- if (current != null) current.setVisibility(GONE);
- if (view != null) view.setVisibility(VISIBLE);
- }
+ if (current != null) ViewUtil.animateOut(current, outAnimation);
+ if (view != null) ViewUtil.animateIn(view, inAnimation);
current = view;
}
-
- private void animateOut(final View view, Animation animation) {
- 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(View view, Animation animation) {
- animation.setInterpolator(new FastOutSlowInInterpolator());
- view.setVisibility(View.VISIBLE);
- view.startAnimation(animation);
- }
}
diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
index 33d5f41d30..c57616f0ce 100644
--- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java
+++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
@@ -91,7 +91,6 @@ public class ThumbnailView extends FrameLayout {
private TransferControlView getTransferControls() {
if (transferControls == null) transferControls = ViewUtil.inflateStub(this, R.id.transfer_controls_stub);
-
return transferControls;
}
@@ -99,9 +98,10 @@ public class ThumbnailView extends FrameLayout {
this.backgroundColorHint = color;
}
- public void setImageResource(@Nullable MasterSecret masterSecret,
- long id, long timestamp,
- @NonNull ListenableFutureTask slideDeckFuture)
+ public void setImageResource(@Nullable MasterSecret masterSecret,
+ long id,
+ long timestamp,
+ @NonNull ListenableFutureTask slideDeckFuture)
{
if (this.slideDeckFuture != null && this.slideDeckListener != null) {
this.slideDeckFuture.removeListener(this.slideDeckListener);
@@ -126,15 +126,17 @@ public class ThumbnailView extends FrameLayout {
Log.w(TAG, "Not re-loading slide " + slide.getPart().getPartId());
return;
}
+
if (!isContextValid()) {
Log.w(TAG, "Not loading slide, context is invalid");
return;
}
- Log.w(TAG, "loading part with id " + slide.getPart().getPartId() + ", progress " + slide.getTransferProgress());
+ Log.w(TAG, "loading part with id " + slide.getPart().getPartId()
+ + ", progress " + slide.getTransferProgress());
this.slide = slide;
- buildGlideRequest(slide, masterSecret).into(image);
+ loadInto(slide, masterSecret, image);
if (this.slide.getTransferProgress() == PartDatabase.TRANSFER_PROGRESS_DONE) {
setOnClickListener(new ThumbnailClickDispatcher());
@@ -145,7 +147,6 @@ public class ThumbnailView extends FrameLayout {
if (!hideControls) {
getTransferControls().setSlide(slide);
getTransferControls().setDownloadClickListener(new DownloadClickDispatcher());
- getTransferControls().setVisibility(View.VISIBLE);
}
}
@@ -189,29 +190,27 @@ public class ThumbnailView extends FrameLayout {
!((Activity)getContext()).isDestroyed();
}
- private GenericRequestBuilder buildGlideRequest(@NonNull Slide slide,
- @Nullable MasterSecret masterSecret)
+ private void loadInto(@NonNull Slide slide,
+ @Nullable MasterSecret masterSecret,
+ @NonNull ImageView view)
{
- final GenericRequestBuilder builder;
if (slide.getThumbnailUri() != null) {
- builder = buildThumbnailGlideRequest(slide, masterSecret);
+ buildThumbnailGlideRequest(slide, masterSecret).into(view);
+ } else if (!slide.isInProgress()) {
+ buildPlaceholderGlideRequest(slide).into(view);
} else {
- builder = buildPlaceholderGlideRequest(slide);
- }
-
- if (slide.getTransferProgress() != PartDatabase.TRANSFER_PROGRESS_DONE && !hideControls) {
- return builder;
- } else {
- return builder.error(R.drawable.ic_missing_thumbnail_picture);
+ Glide.clear(view);
}
}
private GenericRequestBuilder buildThumbnailGlideRequest(Slide slide, MasterSecret masterSecret) {
-
final GenericRequestBuilder builder;
+
if (slide.isDraft()) builder = buildDraftGlideRequest(slide, masterSecret);
else builder = buildPartGlideRequest(slide, masterSecret);
- return builder;
+
+ if (slide.isInProgress()) return builder;
+ else return builder.error(R.drawable.ic_missing_thumbnail_picture);
}
private GenericRequestBuilder buildDraftGlideRequest(Slide slide, MasterSecret masterSecret) {
diff --git a/src/org/thoughtcrime/securesms/components/TransferControlView.java b/src/org/thoughtcrime/securesms/components/TransferControlView.java
index c2406e1b00..282a7ae6aa 100644
--- a/src/org/thoughtcrime/securesms/components/TransferControlView.java
+++ b/src/org/thoughtcrime/securesms/components/TransferControlView.java
@@ -3,9 +3,18 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
-import android.widget.ImageButton;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.ValueAnimator;
+import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.R;
@@ -17,10 +26,18 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import de.greenrobot.event.EventBus;
-public class TransferControlView extends AnimatingToggle {
- private Slide slide;
- private ProgressWheel progressWheel;
- private ImageButton downloadButton;
+public class TransferControlView extends FrameLayout {
+ private static final int TRANSITION_MS = 300;
+
+ @Nullable private Slide slide;
+ @Nullable private View current;
+
+ private final ProgressWheel progressWheel;
+ private final TextView downloadDetails;
+ private final Animation inAnimation;
+ private final Animation outAnimation;
+ private final int contractedWidth;
+ private final int expandedWidth;
public TransferControlView(Context context) {
this(context, null);
@@ -33,8 +50,18 @@ public class TransferControlView extends AnimatingToggle {
public TransferControlView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.transfer_controls_view, this);
- this.progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
- this.downloadButton = ViewUtil.findById(this, R.id.download_button);
+ setBackgroundResource(R.drawable.transfer_controls_background);
+ setVisibility(GONE);
+ this.progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
+ this.downloadDetails = ViewUtil.findById(this, R.id.download_details);
+ this.contractedWidth = getResources().getDimensionPixelSize(R.dimen.transfer_controls_contracted_width);
+ this.expandedWidth = getResources().getDimensionPixelSize(R.dimen.transfer_controls_expanded_width);
+ this.outAnimation = new AlphaAnimation(1f, 0f);
+ this.inAnimation = new AlphaAnimation(0f, 1f);
+ this.outAnimation.setInterpolator(new FastOutSlowInInterpolator());
+ this.inAnimation.setInterpolator(new FastOutSlowInInterpolator());
+ this.outAnimation.setDuration(TRANSITION_MS);
+ this.inAnimation.setDuration(TRANSITION_MS);
}
@Override protected void onAttachedToWindow() {
@@ -49,13 +76,13 @@ public class TransferControlView extends AnimatingToggle {
public void setSlide(final @NonNull Slide slide) {
this.slide = slide;
-
if (slide.getTransferProgress() == PartDatabase.TRANSFER_PROGRESS_STARTED) {
showProgressSpinner();
} else if (slide.isPendingDownload()) {
- display(downloadButton);
+ downloadDetails.setText(slide.getContentDescription());
+ display(downloadDetails);
} else {
- display(null, false);
+ display(null);
}
}
@@ -65,12 +92,57 @@ public class TransferControlView extends AnimatingToggle {
}
public void setDownloadClickListener(final @Nullable OnClickListener listener) {
- downloadButton.setOnClickListener(listener);
+ downloadDetails.setOnClickListener(listener);
}
public void clear() {
- display(null, false);
- slide = null;
+ clearAnimation();
+ setVisibility(GONE);
+ if (current != null) {
+ current.clearAnimation();
+ current.setVisibility(GONE);
+ }
+ current = null;
+ slide = null;
+ }
+
+ private void display(@Nullable final View view) {
+ final int sourceWidth = current == downloadDetails ? expandedWidth : contractedWidth;
+ final int targetWidth = view == downloadDetails ? expandedWidth : contractedWidth;
+
+ if (current == view || current == null) {
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ layoutParams.width = targetWidth;
+ setLayoutParams(layoutParams);
+ } else {
+ ViewUtil.animateOut(current, outAnimation);
+ Animator anim = getWidthAnimator(sourceWidth, targetWidth);
+ anim.start();
+ }
+
+ if (view == null) {
+ ViewUtil.animateOut(this, outAnimation);
+ } else {
+ ViewUtil.animateIn(this, inAnimation);
+ ViewUtil.animateIn(view, inAnimation);
+ }
+
+ current = view;
+ }
+
+ private Animator getWidthAnimator(final int from, final int to) {
+ final ValueAnimator anim = ValueAnimator.ofInt(from, to);
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override public void onAnimationUpdate(ValueAnimator animation) {
+ final int val = (Integer)animation.getAnimatedValue();
+ final ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ layoutParams.width = val;
+ setLayoutParams(layoutParams);
+ }
+ });
+ anim.setInterpolator(new FastOutSlowInInterpolator());
+ anim.setDuration(TRANSITION_MS);
+ return anim;
}
@SuppressWarnings("unused")
@@ -79,7 +151,6 @@ public class TransferControlView extends AnimatingToggle {
Util.runOnMain(new Runnable() {
@Override public void run() {
progressWheel.setInstantProgress(((float)event.progress) / event.total);
- if (event.progress >= event.total) display(null);
}
});
}
diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
index e401918a55..3ede7180eb 100644
--- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -33,6 +33,8 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.Toast;
+import junit.framework.Assert;
+
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
index 78948e65c2..ef4850a7dd 100644
--- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources.Theme;
import android.net.Uri;
import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ResUtil;
@@ -49,6 +50,10 @@ public class AudioSlide extends Slide {
return true;
}
+ @NonNull @Override public String getContentDescription() {
+ return context.getString(R.string.Slide_audio);
+ }
+
@Override
public @DrawableRes int getPlaceholderRes(Theme theme) {
return ResUtil.getDrawableRes(theme, R.attr.conversation_icon_attach_audio);
diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
index 5720a53074..875943dc3a 100644
--- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources.Theme;
import android.net.Uri;
import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
@@ -59,4 +60,8 @@ public class ImageSlide extends Slide {
public boolean hasImage() {
return true;
}
+
+ @NonNull @Override public String getContentDescription() {
+ return context.getString(R.string.Slide_image);
+ }
}
diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java
index f23ff2e653..2dd09a136f 100644
--- a/src/org/thoughtcrime/securesms/mms/Slide.java
+++ b/src/org/thoughtcrime/securesms/mms/Slide.java
@@ -63,6 +63,8 @@ public abstract class Slide {
return false;
}
+ public @NonNull String getContentDescription() { return ""; }
+
public PduPart getPart() {
return part;
}
diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
index 7140a3f795..d243d0c307 100644
--- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources.Theme;
import android.net.Uri;
import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ResUtil;
@@ -53,4 +54,8 @@ public class VideoSlide extends Slide {
public boolean hasVideo() {
return true;
}
+
+ @NonNull @Override public String getContentDescription() {
+ return context.getString(R.string.Slide_video);
+ }
}
diff --git a/src/org/thoughtcrime/securesms/util/ViewUtil.java b/src/org/thoughtcrime/securesms/util/ViewUtil.java
index 9f82db6625..130ec2b7ff 100644
--- a/src/org/thoughtcrime/securesms/util/ViewUtil.java
+++ b/src/org/thoughtcrime/securesms/util/ViewUtil.java
@@ -26,6 +26,7 @@ import android.text.TextUtils.TruncateAt;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.animation.Animation;
import android.widget.TextView;
public class ViewUtil {
@@ -73,4 +74,26 @@ public class ViewUtil {
public static T findById(@NonNull View parent, @IdRes int resId) {
return (T) parent.findViewById(resId);
}
+
+ public static void animateOut(final @NonNull View view, final @NonNull Animation animation) {
+ if (view.getVisibility() == View.GONE) return;
+
+ view.clearAnimation();
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override public void onAnimationStart(Animation animation) {}
+ @Override public void onAnimationRepeat(Animation animation) {}
+ @Override public void onAnimationEnd(Animation animation) {
+ view.setVisibility(View.GONE);
+ }
+ });
+
+ view.startAnimation(animation);
+ }
+
+ public static void animateIn(final @NonNull View view, final @NonNull Animation animation) {
+ if (view.getVisibility() == View.VISIBLE) return;
+ view.clearAnimation();
+ view.setVisibility(View.VISIBLE);
+ view.startAnimation(animation);
+ }
}