diff --git a/build.gradle b/build.gradle
index b3072e0a4b..351d21faf2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,6 +43,8 @@ dependencies {
compile 'com.github.chrisbanes.photoview:library:1.2.3'
compile 'com.github.bumptech.glide:glide:3.6.0'
compile 'com.makeramen:roundedimageview:2.1.0'
+ compile 'com.pnikosis:materialish-progress:1.5'
+ compile 'de.greenrobot:eventbus:2.4.0'
compile ('com.afollestad:material-dialogs:0.7.3.1') {
exclude module: 'appcompat-v7'
exclude module: 'recyclerview-v7'
@@ -72,7 +74,7 @@ dependencies {
compile 'org.whispersystems:jobmanager:0.11.0'
compile 'org.whispersystems:libpastelog:1.0.6'
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
- compile 'org.whispersystems:textsecure-android:1.6.0'
+ compile 'org.whispersystems:textsecure-android:1.6.1'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
@@ -104,6 +106,8 @@ dependencyVerification {
'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc',
'com.github.bumptech.glide:glide:adf657e6bddccb168a29e18ab0954043af46a9b5c736d8c3193c9783fd83d69e',
'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1',
+ 'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
+ 'de.greenrobot:eventbus:61d743a748156a372024d083de763b9e91ac2dcb3f6a1cbc74995c7ddab6e968',
'com.afollestad:material-dialogs:c17205f0d300baa307599c428a5473a6659684c94a5f68ae3c2b84b5e4741172',
'pl.tajchert:waitingdots:2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c',
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
@@ -119,11 +123,11 @@ dependencyVerification {
'org.whispersystems:jobmanager:ea9cb943c4892fb90c1eea1be30efeb85cefca213d52c788419553b58d0ed70d',
'org.whispersystems:libpastelog:550d33c565380d90f4c671e7b8ed5f3a6da55a9fda468373177106b2eb5220b2',
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',
- 'org.whispersystems:textsecure-android:b5786690a2603ca78eed8a4f829737c41e2b5099695ce02bd44d0a9af3392318',
+ 'org.whispersystems:textsecure-android:843d4483e9c3b3414373ddd70df19895b3ee7ef559eeb15e60926e1b07fcecf3',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
- 'org.whispersystems:textsecure-java:dd32ab5fbb232116e7e533a78dce7b8be168bf561c5774772406aea54a677c0a',
+ 'org.whispersystems:textsecure-java:f161c5d5be5a0ba52ede273692ef17982b2af270c6af5c3666bc2adb289a3f61',
'org.whispersystems:axolotl-android:40d3db5004a84749a73f68d2f0d01b2ae35a73c54df96d8c6c6723b96efb6fc0',
'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
diff --git a/res/drawable-hdpi/ic_missing_thumbnail_picture.png b/res/drawable-hdpi/ic_missing_thumbnail_picture.png
index 05cbe34d84..b79b403295 100644
Binary files a/res/drawable-hdpi/ic_missing_thumbnail_picture.png and b/res/drawable-hdpi/ic_missing_thumbnail_picture.png differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim0.png b/res/drawable-hdpi/stat_sys_download_anim0.png
deleted file mode 100644
index 4dca0ecd84..0000000000
Binary files a/res/drawable-hdpi/stat_sys_download_anim0.png and /dev/null differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim1.png b/res/drawable-hdpi/stat_sys_download_anim1.png
deleted file mode 100644
index cf1ac1db87..0000000000
Binary files a/res/drawable-hdpi/stat_sys_download_anim1.png and /dev/null differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim2.png b/res/drawable-hdpi/stat_sys_download_anim2.png
deleted file mode 100644
index 449d2b1270..0000000000
Binary files a/res/drawable-hdpi/stat_sys_download_anim2.png and /dev/null differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim3.png b/res/drawable-hdpi/stat_sys_download_anim3.png
deleted file mode 100644
index 260b2135c2..0000000000
Binary files a/res/drawable-hdpi/stat_sys_download_anim3.png and /dev/null differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim4.png b/res/drawable-hdpi/stat_sys_download_anim4.png
deleted file mode 100644
index 1f5dca42d1..0000000000
Binary files a/res/drawable-hdpi/stat_sys_download_anim4.png and /dev/null differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim5.png b/res/drawable-hdpi/stat_sys_download_anim5.png
deleted file mode 100644
index 98719839c0..0000000000
Binary files a/res/drawable-hdpi/stat_sys_download_anim5.png and /dev/null differ
diff --git a/res/drawable-mdpi/ic_missing_thumbnail_picture.png b/res/drawable-mdpi/ic_missing_thumbnail_picture.png
index e8c712c14b..50e615333b 100644
Binary files a/res/drawable-mdpi/ic_missing_thumbnail_picture.png and b/res/drawable-mdpi/ic_missing_thumbnail_picture.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim0.png b/res/drawable-mdpi/stat_sys_download_anim0.png
deleted file mode 100644
index e6fd9d46ab..0000000000
Binary files a/res/drawable-mdpi/stat_sys_download_anim0.png and /dev/null differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim1.png b/res/drawable-mdpi/stat_sys_download_anim1.png
deleted file mode 100644
index 1c445d7d34..0000000000
Binary files a/res/drawable-mdpi/stat_sys_download_anim1.png and /dev/null differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim2.png b/res/drawable-mdpi/stat_sys_download_anim2.png
deleted file mode 100644
index 8efbe910b4..0000000000
Binary files a/res/drawable-mdpi/stat_sys_download_anim2.png and /dev/null differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim3.png b/res/drawable-mdpi/stat_sys_download_anim3.png
deleted file mode 100644
index 8698c6acf3..0000000000
Binary files a/res/drawable-mdpi/stat_sys_download_anim3.png and /dev/null differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim4.png b/res/drawable-mdpi/stat_sys_download_anim4.png
deleted file mode 100644
index 174836814e..0000000000
Binary files a/res/drawable-mdpi/stat_sys_download_anim4.png and /dev/null differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim5.png b/res/drawable-mdpi/stat_sys_download_anim5.png
deleted file mode 100644
index 27b0617b72..0000000000
Binary files a/res/drawable-mdpi/stat_sys_download_anim5.png and /dev/null differ
diff --git a/res/drawable-xhdpi/ic_missing_thumbnail_picture.png b/res/drawable-xhdpi/ic_missing_thumbnail_picture.png
index 1ace7424cb..e4aaf33a4b 100644
Binary files a/res/drawable-xhdpi/ic_missing_thumbnail_picture.png and b/res/drawable-xhdpi/ic_missing_thumbnail_picture.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim0.png b/res/drawable-xhdpi/stat_sys_download_anim0.png
deleted file mode 100644
index 4c2fdbd17e..0000000000
Binary files a/res/drawable-xhdpi/stat_sys_download_anim0.png and /dev/null differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim1.png b/res/drawable-xhdpi/stat_sys_download_anim1.png
deleted file mode 100644
index 06cb66648d..0000000000
Binary files a/res/drawable-xhdpi/stat_sys_download_anim1.png and /dev/null differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim2.png b/res/drawable-xhdpi/stat_sys_download_anim2.png
deleted file mode 100644
index 75945787b0..0000000000
Binary files a/res/drawable-xhdpi/stat_sys_download_anim2.png and /dev/null differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim3.png b/res/drawable-xhdpi/stat_sys_download_anim3.png
deleted file mode 100644
index 6c715f60a0..0000000000
Binary files a/res/drawable-xhdpi/stat_sys_download_anim3.png and /dev/null differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim4.png b/res/drawable-xhdpi/stat_sys_download_anim4.png
deleted file mode 100644
index e5cfd95ccf..0000000000
Binary files a/res/drawable-xhdpi/stat_sys_download_anim4.png and /dev/null differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim5.png b/res/drawable-xhdpi/stat_sys_download_anim5.png
deleted file mode 100644
index 7756933054..0000000000
Binary files a/res/drawable-xhdpi/stat_sys_download_anim5.png and /dev/null differ
diff --git a/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png b/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png
index 2e2e32e80a..8a04517020 100644
Binary files a/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png and b/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png differ
diff --git a/res/drawable/progress_background.xml b/res/drawable/progress_background.xml
new file mode 100644
index 0000000000..5f2e8b64c9
--- /dev/null
+++ b/res/drawable/progress_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/stat_sys_download.xml b/res/drawable/stat_sys_download.xml
deleted file mode 100644
index 77ecf8588b..0000000000
--- a/res/drawable/stat_sys_download.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index 99be48928d..2444b62c7c 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -46,8 +46,8 @@
diff --git a/res/layout/media_overview_item.xml b/res/layout/media_overview_item.xml
index 8b37e0f125..f02d942140 100644
--- a/res/layout/media_overview_item.xml
+++ b/res/layout/media_overview_item.xml
@@ -1,8 +1,6 @@
-
@@ -10,8 +8,6 @@
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:background="#11ffffff"
android:contentDescription="@string/media_preview_activity__image_content_description" />
-
+
diff --git a/res/layout/thumbnail_view.xml b/res/layout/thumbnail_view.xml
index 7884ac040c..75017775b7 100644
--- a/res/layout/thumbnail_view.xml
+++ b/res/layout/thumbnail_view.xml
@@ -1,22 +1,25 @@
-
+
-
+
-
\ No newline at end of file
+
+
+
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 6c139548eb..6d25609aa4 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -100,12 +100,6 @@
-
-
-
-
-
-
diff --git a/src/org/thoughtcrime/securesms/BaseActionBarActivity.java b/src/org/thoughtcrime/securesms/BaseActionBarActivity.java
index a3dc4c52e5..565bd46f85 100644
--- a/src/org/thoughtcrime/securesms/BaseActionBarActivity.java
+++ b/src/org/thoughtcrime/securesms/BaseActionBarActivity.java
@@ -5,11 +5,14 @@ import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
import java.lang.reflect.Field;
@@ -58,10 +61,8 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
}
protected void startActivitySceneTransition(Intent intent, View sharedView, String transitionName) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, sharedView, transitionName).toBundle());
- } else {
- startActivity(intent);
- }
+ Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(this, sharedView, transitionName)
+ .toBundle();
+ ActivityCompat.startActivity(this, intent, bundle);
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index c2b6cd1e86..300911ce47 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -263,6 +263,7 @@ public class ConversationItem extends LinearLayout {
mediaThumbnail.setImageResource(masterSecret, messageRecord.getId(),
messageRecord.getDateReceived(),
((MediaMmsMessageRecord)messageRecord).getSlideDeckFuture());
+ mediaThumbnail.setShowProgress(!messageRecord.isFailed());
bodyText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
} else {
mediaThumbnail.setVisibility(View.GONE);
@@ -374,7 +375,7 @@ public class ConversationItem extends LinearLayout {
contactPhoto.setAvatar(recipient, true);
contactPhoto.setVisibility(View.VISIBLE);
}
-
+
/// Event handlers
private void handleApproveIdentity() {
diff --git a/src/org/thoughtcrime/securesms/components/ForegroundImageView.java b/src/org/thoughtcrime/securesms/components/ForegroundImageView.java
deleted file mode 100644
index 0139774ff6..0000000000
--- a/src/org/thoughtcrime/securesms/components/ForegroundImageView.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.thoughtcrime.securesms.components;
-
-import android.annotation.TargetApi;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION_CODES;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-
-import com.makeramen.roundedimageview.RoundedImageView;
-
-import org.thoughtcrime.securesms.R;
-
-/**
- * https://gist.github.com/chrisbanes/9091754
- */
-public class ForegroundImageView extends RoundedImageView {
-
- private Drawable mForeground;
-
- private final Rect mSelfBounds = new Rect();
- private final Rect mOverlayBounds = new Rect();
-
- private int mForegroundGravity = Gravity.FILL;
-
- private boolean mForegroundInPadding = true;
-
- private boolean mForegroundBoundsChanged = false;
-
- public ForegroundImageView(Context context) {
- super(context);
- }
-
- public ForegroundImageView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ForegroundImageView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundImageView,
- defStyle, 0);
-
- mForegroundGravity = a.getInt(
- R.styleable.ForegroundImageView_android_foregroundGravity, mForegroundGravity);
-
- final Drawable d = a.getDrawable(R.styleable.ForegroundImageView_android_foreground);
- if (d != null) {
- setForeground(d);
- }
-
- mForegroundInPadding = a.getBoolean(
- R.styleable.ForegroundImageView_android_foregroundInsidePadding, true);
-
- a.recycle();
- }
-
- /**
- * Describes how the foreground is positioned.
- *
- * @return foreground gravity.
- *
- * @see #setForegroundGravity(int)
- */
- public int getForegroundGravity() {
- return mForegroundGravity;
- }
-
- /**
- * Describes how the foreground is positioned. Defaults to START and TOP.
- *
- * @param foregroundGravity See {@link android.view.Gravity}
- *
- * @see #getForegroundGravity()
- */
- public void setForegroundGravity(int foregroundGravity) {
- if (mForegroundGravity != foregroundGravity) {
- if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.START;
- }
-
- if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.TOP;
- }
-
- mForegroundGravity = foregroundGravity;
-
-
- if (mForegroundGravity == Gravity.FILL && mForeground != null) {
- Rect padding = new Rect();
- mForeground.getPadding(padding);
- }
-
- requestLayout();
- }
- }
-
- @TargetApi(VERSION_CODES.JELLY_BEAN)
- public ActivityOptions getThumbnailTransition() {
- return ActivityOptions.makeScaleUpAnimation(this, 0, 0, getWidth(), getHeight());
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || (who == mForeground);
- }
-
- @Override
- @TargetApi(VERSION_CODES.HONEYCOMB)
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- if (mForeground != null) mForeground.jumpToCurrentState();
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- if (mForeground != null && mForeground.isStateful()) {
- mForeground.setState(getDrawableState());
- }
- }
-
- /**
- * Supply a Drawable that is to be rendered on top of all of the child
- * views in the frame layout. Any padding in the Drawable will be taken
- * into account by ensuring that the children are inset to be placed
- * inside of the padding area.
- *
- * @param drawable The Drawable to be drawn on top of the children.
- */
- public void setForeground(Drawable drawable) {
- if (mForeground != drawable) {
- if (mForeground != null) {
- mForeground.setCallback(null);
- unscheduleDrawable(mForeground);
- }
-
- mForeground = drawable;
-
- if (drawable != null) {
- setWillNotDraw(false);
- drawable.setCallback(this);
- if (drawable.isStateful()) {
- drawable.setState(getDrawableState());
- }
- if (mForegroundGravity == Gravity.FILL) {
- Rect padding = new Rect();
- drawable.getPadding(padding);
- }
- } else {
- setWillNotDraw(true);
- }
- requestLayout();
- invalidate();
- }
- }
-
- /**
- * Returns the drawable used as the foreground of this FrameLayout. The
- * foreground drawable, if non-null, is always drawn on top of the children.
- *
- * @return A Drawable or null if no foreground was set.
- */
- public Drawable getForeground() {
- return mForeground;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mForegroundBoundsChanged = changed;
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mForegroundBoundsChanged = true;
- }
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- super.draw(canvas);
-
- if (mForeground != null) {
- final Drawable foreground = mForeground;
-
- if (mForegroundBoundsChanged) {
- mForegroundBoundsChanged = false;
- final Rect selfBounds = mSelfBounds;
- final Rect overlayBounds = mOverlayBounds;
-
- final int w = getRight() - getLeft();
- final int h = getBottom() - getTop();
-
- if (mForegroundInPadding) {
- selfBounds.set(0, 0, w, h);
- } else {
- selfBounds.set(getPaddingLeft(), getPaddingTop(),
- w - getPaddingRight(), h - getPaddingBottom());
- }
-
- Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
- foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
- foreground.setBounds(overlayBounds);
- }
-
- foreground.draw(canvas);
- }
- }
-}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java b/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java
new file mode 100644
index 0000000000..b4028febf1
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java
@@ -0,0 +1,35 @@
+package org.thoughtcrime.securesms.components;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class SquareFrameLayout extends FrameLayout {
+ @SuppressWarnings("unused")
+ public SquareFrameLayout(Context context) {
+ super(context);
+ }
+
+ @SuppressWarnings("unused")
+ public SquareFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @TargetApi(VERSION_CODES.HONEYCOMB) @SuppressWarnings("unused")
+ public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @TargetApi(VERSION_CODES.LOLLIPOP) @SuppressWarnings("unused")
+ public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ //noinspection SuspiciousNameCombination
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
index 3f58cf7ac4..4d580995bd 100644
--- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java
+++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
@@ -4,56 +4,87 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.widget.FrameLayout;
import com.bumptech.glide.GenericRequestBuilder;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.makeramen.roundedimageview.RoundedImageView;
+import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.jobs.PartProgressEvent;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
-import org.thoughtcrime.securesms.mms.ThumbnailTransform;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Util;
+import de.greenrobot.event.EventBus;
import ws.com.google.android.mms.pdu.PduPart;
-public class ThumbnailView extends RoundedImageView {
+public class ThumbnailView extends FrameLayout {
+ private static final String TAG = ThumbnailView.class.getSimpleName();
+
+ private boolean showProgress = true;
+ private RoundedImageView image;
+ private ProgressWheel progress;
private ListenableFutureTask slideDeckFuture = null;
private SlideDeckListener slideDeckListener = null;
private ThumbnailClickListener thumbnailClickListener = null;
private String slideId = null;
private Slide slide = null;
- private Handler handler = new Handler();
public ThumbnailView(Context context) {
- super(context);
+ this(context, null);
}
public ThumbnailView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public ThumbnailView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ inflate(context, R.layout.thumbnail_view, this);
+ image = (RoundedImageView) findViewById(R.id.thumbnail_image);
+ progress = (ProgressWheel) findViewById(R.id.progress_wheel);
+ }
+
+ @Override protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ EventBus.getDefault().registerSticky(this);
+ }
+
+ @Override protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ EventBus.getDefault().unregister(this);
+ }
+
+ @SuppressWarnings("unused")
+ public void onEventAsync(final PartProgressEvent event) {
+ if (this.slide != null && event.partId.equals(this.slide.getPart().getPartId())) {
+ Util.runOnMain(new Runnable() {
+ @Override public void run() {
+ progress.setInstantProgress(((float) event.progress) / event.total);
+ if (event.progress >= event.total) animateOutProgress();
+ }
+ });
+ }
}
public void setImageResource(@Nullable MasterSecret masterSecret,
@@ -67,7 +98,7 @@ public class ThumbnailView extends RoundedImageView {
String slideId = id + "::" + timestamp;
if (!slideId.equals(this.slideId)) {
- setImageDrawable(null);
+ image.setImageDrawable(null);
this.slide = null;
this.slideId = slideId;
}
@@ -82,13 +113,24 @@ public class ThumbnailView extends RoundedImageView {
}
public void setImageResource(@NonNull Slide slide, @Nullable MasterSecret masterSecret) {
- if (isContextValid()) {
- if (!Util.equals(slide, this.slide)) buildGlideRequest(slide, masterSecret).into(this);
- this.slide = slide;
- setOnClickListener(new ThumbnailClickDispatcher(thumbnailClickListener, slide));
- } else {
- Log.w(TAG, "Not going to load resource, context is invalid");
+ if (Util.equals(slide, this.slide)) {
+ Log.w(TAG, "Not loading resource, slide was identical");
+ return;
}
+ if (!isContextValid()) {
+ Log.w(TAG, "Not loading resource, context is invalid");
+ return;
+ }
+
+ this.slide = slide;
+ if (slide.isInProgress() && showProgress) {
+ progress.spin();
+ progress.setVisibility(VISIBLE);
+ } else {
+ progress.setVisibility(GONE);
+ }
+ buildGlideRequest(slide, masterSecret).into(image);
+ setOnClickListener(new ThumbnailClickDispatcher(thumbnailClickListener, slide));
}
public void setThumbnailClickListener(ThumbnailClickListener listener) {
@@ -99,6 +141,13 @@ public class ThumbnailView extends RoundedImageView {
if (isContextValid()) Glide.clear(this);
}
+ public void setShowProgress(boolean showProgress) {
+ this.showProgress = showProgress;
+ if (progress.getVisibility() == View.VISIBLE && !showProgress) {
+ animateOutProgress();
+ }
+ }
+
@TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
private boolean isContextValid() {
return !(getContext() instanceof Activity) ||
@@ -110,22 +159,17 @@ public class ThumbnailView extends RoundedImageView {
@Nullable MasterSecret masterSecret)
{
final GenericRequestBuilder builder;
- if (slide.getPart().isPendingPush()) {
- builder = buildPendingGlideRequest(slide);
- } else if (slide.getThumbnailUri() != null) {
+ if (slide.getThumbnailUri() != null) {
builder = buildThumbnailGlideRequest(slide, masterSecret);
} else {
builder = buildPlaceholderGlideRequest(slide);
}
- return builder.error(R.drawable.ic_missing_thumbnail_picture);
- }
-
- private GenericRequestBuilder buildPendingGlideRequest(Slide slide) {
- return Glide.with(getContext()).load(R.drawable.stat_sys_download_anim0)
- .dontTransform()
- .skipMemoryCache(true)
- .crossFade();
+ if (slide.isInProgress() && showProgress) {
+ return builder;
+ } else {
+ return builder.error(R.drawable.ic_missing_thumbnail_picture);
+ }
}
private GenericRequestBuilder buildThumbnailGlideRequest(Slide slide, MasterSecret masterSecret) {
@@ -148,7 +192,7 @@ public class ThumbnailView extends RoundedImageView {
}
return Glide.with(getContext()).load(new DecryptableUri(masterSecret, slide.getThumbnailUri()))
- .transform(new ThumbnailTransform(getContext()));
+ .centerCrop();
}
private GenericRequestBuilder buildPlaceholderGlideRequest(Slide slide) {
@@ -157,6 +201,19 @@ public class ThumbnailView extends RoundedImageView {
.crossFade();
}
+ private void animateOutProgress() {
+ AlphaAnimation animation = new AlphaAnimation(1f, 0f);
+ animation.setDuration(200);
+ animation.setAnimationListener(new AnimationListener() {
+ @Override public void onAnimationStart(Animation animation) { }
+ @Override public void onAnimationRepeat(Animation animation) { }
+ @Override public void onAnimationEnd(Animation animation) {
+ progress.setVisibility(View.GONE);
+ }
+ });
+ progress.startAnimation(animation);
+ }
+
private class SlideDeckListener implements FutureTaskListener {
private final MasterSecret masterSecret;
@@ -170,14 +227,14 @@ public class ThumbnailView extends RoundedImageView {
final Slide slide = slideDeck.getThumbnailSlide(getContext());
if (slide != null) {
- handler.post(new Runnable() {
+ Util.runOnMain(new Runnable() {
@Override
public void run() {
setImageResource(slide, masterSecret);
}
});
} else {
- handler.post(new Runnable() {
+ Util.runOnMain(new Runnable() {
@Override
public void run() {
Log.w(TAG, "Resolved slide was null!");
@@ -190,7 +247,7 @@ public class ThumbnailView extends RoundedImageView {
@Override
public void onFailure(Throwable error) {
Log.w(TAG, error);
- handler.post(new Runnable() {
+ Util.runOnMain(new Runnable() {
@Override
public void run() {
Log.w(TAG, "onFailure!");
diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
index 7b8a091003..8ec2419a63 100644
--- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -721,6 +721,12 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
contentValues.remove(ADDRESS);
+ if (sendRequest.getBody() != null) {
+ for (int i = 0; i < sendRequest.getBody().getPartsNum(); i++) {
+ sendRequest.getBody().getPart(i).setInProgress(true);
+ }
+ }
+
long messageId = insertMediaMessage(masterSecret, sendRequest.getPduHeaders(),
sendRequest.getBody(), contentValues);
jobManager.add(new TrimThreadJob(context, threadId));
diff --git a/src/org/thoughtcrime/securesms/database/PartDatabase.java b/src/org/thoughtcrime/securesms/database/PartDatabase.java
index 938f2036e4..5207ed8eb2 100644
--- a/src/org/thoughtcrime/securesms/database/PartDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/PartDatabase.java
@@ -57,26 +57,26 @@ import ws.com.google.android.mms.pdu.PduPart;
public class PartDatabase extends Database {
private static final String TAG = PartDatabase.class.getSimpleName();
- private static final String TABLE_NAME = "part";
- private static final String ROW_ID = "_id";
- private static final String MMS_ID = "mid";
- private static final String SEQUENCE = "seq";
- private static final String CONTENT_TYPE = "ct";
- private static final String NAME = "name";
- private static final String CHARSET = "chset";
- private static final String CONTENT_DISPOSITION = "cd";
- private static final String FILENAME = "fn";
- private static final String CONTENT_ID = "cid";
- private static final String CONTENT_LOCATION = "cl";
- private static final String CONTENT_TYPE_START = "ctt_s";
- private static final String CONTENT_TYPE_TYPE = "ctt_t";
- private static final String ENCRYPTED = "encrypted";
- private static final String DATA = "_data";
- private static final String PENDING_PUSH_ATTACHMENT = "pending_push";
- private static final String SIZE = "data_size";
- private static final String THUMBNAIL = "thumbnail";
- private static final String ASPECT_RATIO = "aspect_ratio";
- private static final String UNIQUE_ID = "unique_id";
+ private static final String TABLE_NAME = "part";
+ private static final String ROW_ID = "_id";
+ private static final String MMS_ID = "mid";
+ private static final String SEQUENCE = "seq";
+ private static final String CONTENT_TYPE = "ct";
+ private static final String NAME = "name";
+ private static final String CHARSET = "chset";
+ private static final String CONTENT_DISPOSITION = "cd";
+ private static final String FILENAME = "fn";
+ private static final String CONTENT_ID = "cid";
+ private static final String CONTENT_LOCATION = "cl";
+ private static final String CONTENT_TYPE_START = "ctt_s";
+ private static final String CONTENT_TYPE_TYPE = "ctt_t";
+ private static final String ENCRYPTED = "encrypted";
+ private static final String DATA = "_data";
+ private static final String IN_PROGRESS = "pending_push";
+ private static final String SIZE = "data_size";
+ private static final String THUMBNAIL = "thumbnail";
+ private static final String ASPECT_RATIO = "aspect_ratio";
+ private static final String UNIQUE_ID = "unique_id";
private static final String PART_ID_WHERE = ROW_ID + " = ? AND " + UNIQUE_ID + " = ?";
@@ -86,12 +86,12 @@ public class PartDatabase extends Database {
CONTENT_DISPOSITION + " TEXT, " + FILENAME + " TEXT, " + CONTENT_ID + " TEXT, " +
CONTENT_LOCATION + " TEXT, " + CONTENT_TYPE_START + " INTEGER, " +
CONTENT_TYPE_TYPE + " TEXT, " + ENCRYPTED + " INTEGER, " +
- PENDING_PUSH_ATTACHMENT + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
+ IN_PROGRESS + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
THUMBNAIL + " TEXT, " + ASPECT_RATIO + " REAL, " + UNIQUE_ID + " INTEGER NOT NULL);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
- "CREATE INDEX IF NOT EXISTS pending_push_index ON " + TABLE_NAME + " (" + PENDING_PUSH_ATTACHMENT + ");",
+ "CREATE INDEX IF NOT EXISTS pending_push_index ON " + TABLE_NAME + " (" + IN_PROGRESS + ");",
};
private final static String IMAGES_QUERY = "SELECT " + TABLE_NAME + "." + ROW_ID + ", "
@@ -127,7 +127,7 @@ public class PartDatabase extends Database {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
part.setContentDisposition(new byte[0]);
- part.setPendingPush(false);
+ part.setInProgress(false);
ContentValues values = getContentValuesForPart(part);
@@ -275,10 +275,10 @@ public class PartDatabase extends Database {
if (!cursor.isNull(encryptedColumn))
part.setEncrypted(cursor.getInt(encryptedColumn) == 1);
- int pendingPushColumn = cursor.getColumnIndexOrThrow(PENDING_PUSH_ATTACHMENT);
+ int inProgressColumn = cursor.getColumnIndexOrThrow(IN_PROGRESS);
- if (!cursor.isNull(pendingPushColumn))
- part.setPendingPush(cursor.getInt(pendingPushColumn) == 1);
+ if (!cursor.isNull(inProgressColumn))
+ part.setInProgress(cursor.getInt(inProgressColumn) == 1);
int sizeColumn = cursor.getColumnIndexOrThrow(SIZE);
@@ -325,7 +325,7 @@ public class PartDatabase extends Database {
}
contentValues.put(ENCRYPTED, part.getEncrypted() ? 1 : 0);
- contentValues.put(PENDING_PUSH_ATTACHMENT, part.isPendingPush() ? 1 : 0);
+ contentValues.put(IN_PROGRESS, part.isInProgress() ? 1 : 0);
contentValues.put(UNIQUE_ID, part.getUniqueId());
return contentValues;
@@ -437,7 +437,7 @@ public class PartDatabase extends Database {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Pair partData = null;
- if (!part.isPendingPush()) {
+ if (part.getData() != null || part.getDataUri() != null) {
partData = writePartData(masterSecret, part);
Log.w(TAG, "Wrote part to file: " + partData.first.getAbsolutePath());
}
@@ -457,7 +457,7 @@ public class PartDatabase extends Database {
Log.w(TAG, "inserting pre-generated thumbnail");
ThumbnailData data = new ThumbnailData(thumbnail);
updatePartThumbnail(masterSecret, partId, part, data.toDataStream(), data.getAspectRatio());
- } else if (!part.isPendingPush()) {
+ } else if (!part.isInProgress()) {
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, partId));
}
@@ -472,7 +472,7 @@ public class PartDatabase extends Database {
Pair partData = writePartData(masterSecret, part, data);
part.setContentDisposition(new byte[0]);
- part.setPendingPush(false);
+ part.setInProgress(false);
ContentValues values = getContentValuesForPart(part);
@@ -488,6 +488,17 @@ public class PartDatabase extends Database {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
}
+ public void markPartUploaded(long messageId, PduPart part) {
+ ContentValues values = new ContentValues(1);
+ SQLiteDatabase database = databaseHelper.getWritableDatabase();
+
+ part.setInProgress(false);
+ values.put(IN_PROGRESS, false);
+ database.update(TABLE_NAME, values, PART_ID_WHERE, part.getPartId().toStrings());
+
+ notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
+ }
+
public void updatePartData(MasterSecret masterSecret, PduPart part, InputStream data)
throws MmsException
{
@@ -640,5 +651,20 @@ public class PartDatabase extends Database {
public boolean isValid() {
return rowId >= 0 && uniqueId >= 0;
}
+
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PartId partId = (PartId)o;
+
+ if (rowId != partId.rowId) return false;
+ return uniqueId == partId.uniqueId;
+
+ }
+
+ @Override public int hashCode() {
+ return Util.hashCode(rowId, uniqueId);
+ }
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
index 4402aa4a17..85bce1f142 100644
--- a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
@@ -3,10 +3,12 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.util.Log;
+
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.PartDatabase;
+import org.thoughtcrime.securesms.database.PartDatabase.PartId;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.util.Base64;
@@ -15,6 +17,7 @@ import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.textsecure.api.TextSecureMessageReceiver;
+import org.whispersystems.textsecure.api.messages.TextSecureAttachment.ProgressListener;
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer;
import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.textsecure.api.push.exceptions.PushNetworkException;
@@ -26,6 +29,7 @@ import java.util.List;
import javax.inject.Inject;
+import de.greenrobot.event.EventBus;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.PduPart;
@@ -82,15 +86,19 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
private void retrievePart(MasterSecret masterSecret, PduPart part, long messageId)
throws IOException
{
- PartDatabase database = DatabaseFactory.getPartDatabase(context);
- File attachmentFile = null;
- PartDatabase.PartId partId = part.getPartId();
+ PartDatabase database = DatabaseFactory.getPartDatabase(context);
+ File attachmentFile = null;
+ final PartId partId = part.getPartId();
try {
attachmentFile = createTempFile();
TextSecureAttachmentPointer pointer = createAttachmentPointer(masterSecret, part);
- InputStream attachment = messageReceiver.retrieveAttachment(pointer, attachmentFile);
+ InputStream attachment = messageReceiver.retrieveAttachment(pointer, attachmentFile, new ProgressListener() {
+ @Override public void onAttachmentProgress(long total, long progress) {
+ EventBus.getDefault().postSticky(new PartProgressEvent(partId, total, progress));
+ }
+ });
database.updateDownloadedPart(masterSecret, messageId, partId, part, attachment);
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) {
@@ -145,4 +153,5 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
private class InvalidPartException extends Exception {
public InvalidPartException(Exception e) {super(e);}
}
+
}
diff --git a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java
index c85d1fe1bd..f3a5b14670 100644
--- a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java
@@ -96,7 +96,7 @@ public class AvatarDownloadJob extends MasterSecretJob {
destination.deleteOnExit();
- socket.retrieveAttachment(relay, contentLocation, destination);
+ socket.retrieveAttachment(relay, contentLocation, destination, null);
return destination;
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
index 3b36e6d48d..90b78b906d 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
@@ -103,7 +103,8 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
FileInputStream contactsFileStream = new FileInputStream(contactsFile);
TextSecureAttachmentStream attachmentStream = new TextSecureAttachmentStream(contactsFileStream,
"application/octet-stream",
- contactsFile.length());
+ contactsFile.length(),
+ null);
try {
messageSender.sendMessage(TextSecureSyncMessage.forContacts(attachmentStream));
@@ -117,7 +118,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
try {
Uri displayPhotoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r");
- return Optional.of(new TextSecureAttachmentStream(fd.createInputStream(), "image/*", fd.getLength()));
+ return Optional.of(new TextSecureAttachmentStream(fd.createInputStream(), "image/*", fd.getLength(), null));
} catch (IOException e) {
Log.w(TAG, e);
}
@@ -140,7 +141,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
byte[] data = cursor.getBlob(0);
if (data != null) {
- return Optional.of(new TextSecureAttachmentStream(new ByteArrayInputStream(data), "image/*", data.length));
+ return Optional.of(new TextSecureAttachmentStream(new ByteArrayInputStream(data), "image/*", data.length, null));
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
index 7f8b1e9cd5..5fdaf13f92 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
@@ -95,7 +95,8 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
FileInputStream contactsFileStream = new FileInputStream(contactsFile);
TextSecureAttachmentStream attachmentStream = new TextSecureAttachmentStream(contactsFileStream,
"application/octet-stream",
- contactsFile.length());
+ contactsFile.length(),
+ null);
messageSender.sendMessage(TextSecureSyncMessage.forGroups(attachmentStream));
}
@@ -105,7 +106,7 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
if (avatar == null) return Optional.absent();
return Optional.of(new TextSecureAttachmentStream(new ByteArrayInputStream(avatar),
- "image/*", avatar.length));
+ "image/*", avatar.length, null));
}
private File createTempFile(String prefix) throws IOException {
diff --git a/src/org/thoughtcrime/securesms/jobs/PartProgressEvent.java b/src/org/thoughtcrime/securesms/jobs/PartProgressEvent.java
new file mode 100644
index 0000000000..7a22aaef6b
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/jobs/PartProgressEvent.java
@@ -0,0 +1,15 @@
+package org.thoughtcrime.securesms.jobs;
+
+import org.thoughtcrime.securesms.database.PartDatabase.PartId;
+
+public class PartProgressEvent {
+ public PartId partId;
+ public long total;
+ public long progress;
+
+ public PartProgressEvent(PartId partId, long total, long progress) {
+ this.partId = partId;
+ this.total = total;
+ this.progress = progress;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
index ead5f892c2..1fbe8d51a1 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
@@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
+import org.thoughtcrime.securesms.database.PartDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.PartParser;
@@ -30,6 +31,7 @@ import java.util.List;
import javax.inject.Inject;
import ws.com.google.android.mms.MmsException;
+import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.SendReq;
import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory;
@@ -69,6 +71,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
database.markAsPush(messageId);
database.markAsSecure(messageId);
database.markAsSent(messageId, "push".getBytes(), 0);
+ updatePartsStatus(message.getBody());
} catch (InsecureFallbackApprovalException ifae) {
Log.w(TAG, ifae);
database.markAsPendingInsecureSmsFallback(messageId);
@@ -97,6 +100,13 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
notifyMediaMessageDeliveryFailed(context, messageId);
}
+ private void updatePartsStatus(PduBody body) {
+ if (body == null) return;
+ PartDatabase database = DatabaseFactory.getPartDatabase(context);
+ for (int i = 0; i < body.getPartsNum(); i++) {
+ database.markPartUploaded(messageId, body.getPart(i));
+ }
+ }
private void deliver(MasterSecret masterSecret, SendReq message)
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java
index 027148092f..bddae1c39d 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java
@@ -10,13 +10,12 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
-import org.thoughtcrime.securesms.util.GroupUtil;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
+import org.whispersystems.textsecure.api.messages.TextSecureAttachment.ProgressListener;
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentStream;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
import org.whispersystems.textsecure.api.util.InvalidNumberException;
@@ -26,6 +25,7 @@ import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
+import de.greenrobot.event.EventBus;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.SendReq;
@@ -59,16 +59,19 @@ public abstract class PushSendJob extends SendJob {
List attachments = new LinkedList<>();
for (int i=0;i