Compare commits

...

11 Commits

Author SHA1 Message Date
Moxie Marlinspike
4316fefe61 Bump version to 4.12.1
// FREEBIE
2017-11-01 15:16:41 -07:00
Moxie Marlinspike
14b750d202 Bump version to 4.12.0
// FREEBIE
2017-10-11 17:47:12 -07:00
Moxie Marlinspike
7b0a0eccd0 Update Dockerfile for compileSdk 26
// FREEBIE
2017-10-11 17:47:12 -07:00
Christian Ascheberg
884c875c84 Add back a way to access quick contact from conversation
Closes #7051
// FREEBIE
2017-10-11 17:47:12 -07:00
Greg Cooksey
83f15e231b CreateProfileActivity uses DynamicTheme
Fixes #6981
Closes #6997
// FREEBIE
2017-10-11 17:47:12 -07:00
Christian Ascheberg
677b2a7494 Do not show sent checkmark for verification status changes
Fixes #6743
Closes #7041
// FREEBIE
2017-10-11 17:47:12 -07:00
Nicholas Rizzio
7016f75886 Add a space between group member names
Fixes #7059
Closes #7060
// FREEBIE
2017-10-11 17:47:12 -07:00
haffenloher
76c6af6537 Fix synced sent media messages expiring too fast
Fixes #6928
Closes #7135
2017-10-11 17:47:12 -07:00
Moxie Marlinspike
81e5e851f7 Update to glide 4.3.0
// FREEBIE
2017-10-11 17:47:12 -07:00
Moxie Marlinspike
2ae890ef57 Update to libsignal-service 2.6.11
// FREEBIE
2017-10-11 17:47:12 -07:00
Moxie Marlinspike
5fadc88646 Update giphy connectivity strategy for glide
// FREEBIE
2017-10-11 17:47:12 -07:00
19 changed files with 503 additions and 37 deletions

View File

@@ -11,7 +11,7 @@ RUN dpkg --add-architecture i386 && \
ENV ANDROID_SDK_FILENAME android-sdk_r24.4.1-linux.tgz
ENV ANDROID_SDK_URL https://dl.google.com/android/${ANDROID_SDK_FILENAME}
ENV ANDROID_API_LEVELS android-25
ENV ANDROID_API_LEVELS android-26
ENV ANDROID_BUILD_TOOLS_VERSION 25.0.2
ENV ANDROID_HOME /usr/local/android-sdk-linux
ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools

View File

@@ -63,7 +63,7 @@ dependencies {
compile 'org.whispersystems:jobmanager:1.0.2'
compile 'org.whispersystems:libpastelog:1.0.7'
compile 'org.whispersystems:signal-service-android:2.6.10'
compile 'org.whispersystems:signal-service-android:2.6.11'
compile 'org.whispersystems:webrtc-android:M59-S1'
compile "me.leolin:ShortcutBadger:1.1.16"
@@ -71,8 +71,8 @@ dependencies {
compile 'com.jpardogo.materialtabstrip:library:1.0.9'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
compile 'com.github.chrisbanes:PhotoView:2.1.3'
compile 'com.github.bumptech.glide:glide:4.2.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.2.0'
compile 'com.github.bumptech.glide:glide:4.3.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.3.0'
compile 'com.makeramen:roundedimageview:2.1.0'
compile 'com.pnikosis:materialish-progress:1.5'
compile 'org.greenrobot:eventbus:3.0.0'
@@ -147,14 +147,14 @@ dependencyVerification {
'com.google.android.exoplayer:exoplayer:955085aa611a8f7cf6c61b88ae03d1a392f4ad94c9bfbc153f3dedb9ffb14718',
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88',
'org.whispersystems:signal-service-android:784b748c15cb3d2824b8fa2b0f9d8fd8b990b39e7a50259bac27bdcad845ce20',
'org.whispersystems:signal-service-android:89f8630cc1737c3d52178dc46926f0755d75fed3ac9b94d067c0a42e4e3169c9',
'org.whispersystems:webrtc-android:de647643afbbea45a26a4f24db75aa10bc8de45426e8eb0d9d563cc10af4f582',
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
'com.github.chrisbanes:PhotoView:ed06775308da260e1fd86d1d3288988fcd3d80db24ce0d7c9fcfedc39e622292',
'com.github.bumptech.glide:glide:555350c4b9d163f1d3772a64a92119086073ed88340eb284391b1acc1bb5dd6c',
'com.github.bumptech.glide:glide:cf770a66bdb42d90663672a3e44b8e4f4fb060073294af5ebd323c5db415b22f',
'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1',
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
'org.greenrobot:eventbus:180d4212467df06f2fbc9c8d8a2984533ac79c87769ad883bc421612f0b4e17c',
@@ -185,10 +185,10 @@ dependencyVerification {
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
'org.whispersystems:signal-service-java:308d9e61b753760d0f3828eb3181db58469e75c763bdce5a8335df6c4af47695',
'com.github.bumptech.glide:gifdecoder:217da4520c568a93aea9c7ce3b3cac2c61fabed5113b07ae38698054f6d2d8b6',
'com.github.bumptech.glide:disklrucache:795c13245498c0cd806c3af71ee57b3f179cbd1609440a3021c211c364ef74d3',
'com.github.bumptech.glide:annotations:057927a236f3229e72cfbac8bed0e9fb398473daf7d933390f59ea4cb79c137b',
'org.whispersystems:signal-service-java:ef89da56b915490bb907d848eae79efdf1218e985763e7dd2e8047c7ccb03c0c',
'com.github.bumptech.glide:gifdecoder:fe793861d4d4619b5041d3bd68186000b6151581292053e88c96a5d0b60e5337',
'com.github.bumptech.glide:disklrucache:b5cf8f76b423a6c86edbe82380958adbe6a2f1d5afbd6add23a9c8ad141eb406',
'com.github.bumptech.glide:annotations:10a910f62ee27de5f0e44a72acb7fe31ed1e45b3ffac82fb3a8ebada150765f1',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
@@ -225,8 +225,8 @@ android {
}
defaultConfig {
versionCode 304
versionName "4.11.5"
versionCode 306
versionName "4.12.1"
minSdkVersion 14
targetSdkVersion 22

View File

@@ -856,6 +856,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
private boolean handleDisplayQuickContact() {
if (recipient.getAddress().isGroup()) return false;
if (recipient.getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
} else {
handleAddToContacts();
}
return true;
}
private void handleAddAttachment() {
if (this.isMmsEnabled || isSecureText) {
if (attachmentTypeSelector == null) {
@@ -1209,6 +1221,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
});
titleView.setOnClickListener(v -> handleConversationSettings());
titleView.setOnLongClickListener(v -> handleDisplayQuickContact());
titleView.setOnBackClickedListener(view -> super.onBackPressed());
unblockButton.setOnClickListener(v -> handleUnblock());
makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms());

View File

@@ -207,7 +207,7 @@ public class ConversationListItem extends RelativeLayout
}
private void setStatusIcons(ThreadRecord thread) {
if (!thread.isOutgoing() || thread.isOutgoingCall()) {
if (!thread.isOutgoing() || thread.isOutgoingCall() || thread.isVerificationStatusChange()) {
deliveryStatusIndicator.setNone();
alertView.setNone();
} else if (thread.isFailed()) {

View File

@@ -84,6 +84,12 @@ public class ConversationTitleView extends RelativeLayout {
this.avatar.setOnClickListener(listener);
}
@Override
public void setOnLongClickListener(@Nullable OnLongClickListener listener) {
this.content.setOnLongClickListener(listener);
this.avatar.setOnLongClickListener(listener);
}
public void setOnBackClickedListener(@Nullable OnClickListener listener) {
this.back.setOnClickListener(listener);
}
@@ -107,7 +113,7 @@ public class ConversationTitleView extends RelativeLayout {
this.subtitle.setText(Stream.of(recipient.getParticipants())
.filter(r -> !r.getAddress().serialize().equals(localNumber))
.map(Recipient::toShortString)
.collect(Collectors.joining(",")));
.collect(Collectors.joining(", ")));
this.subtitle.setVisibility(View.VISIBLE);
}

View File

@@ -43,6 +43,8 @@ import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
import org.thoughtcrime.securesms.profiles.SystemProfileUtil;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@@ -73,6 +75,9 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
private static final int REQUEST_CODE_AVATAR = 1;
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@Inject SignalServiceAccountManager accountManager;
private InputAwareLayout container;
@@ -91,6 +96,9 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
setContentView(R.layout.profile_create_activity);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
@@ -104,6 +112,13 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
ApplicationContext.getInstance(this).injectDependencies(this);
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
@Override
public void onBackPressed() {
if (container.isInputOpen()) container.hideCurrentInput(name);

View File

@@ -146,6 +146,7 @@ public class ThumbnailView extends FrameLayout {
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(new RoundedCorners(radius))
.transition(withCrossFade())
.centerCrop()
.into(image);
}
@@ -175,6 +176,7 @@ public class ThumbnailView extends FrameLayout {
RequestBuilder builder = glideRequests.load(new DecryptableUri(masterSecret, slide.getThumbnailUri()))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(new RoundedCorners(radius))
.centerCrop()
.transition(withCrossFade());
if (slide.isInProgress()) return builder;

View File

@@ -144,6 +144,10 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isMissedCall(type);
}
public boolean isVerificationStatusChange() {
return SmsDatabase.Types.isIdentityDefault(type) || SmsDatabase.Types.isIdentityVerified(type);
}
public int getDeliveryStatus() {
return deliveryStatus;
}

View File

@@ -12,10 +12,18 @@ public class GiphyImage {
return images.downsized.url;
}
public long getGifSize() {
return images.downsized.size;
}
public String getGifMmsUrl() {
return images.fixed_height_downsampled.url;
}
public long getMmsGifSize() {
return images.fixed_height_downsampled.size;
}
public float getGifAspectRatio() {
return (float)images.downsized.width / (float)images.downsized.height;
}
@@ -24,6 +32,10 @@ public class GiphyImage {
return images.downsized_still.url;
}
public long getStillSize() {
return images.downsized_still.size;
}
public static class ImageTypes {
@JsonProperty
private ImageData fixed_height;

View File

@@ -0,0 +1,50 @@
package org.thoughtcrime.securesms.giph.model;
import android.support.annotation.NonNull;
import com.bumptech.glide.load.Key;
import org.thoughtcrime.securesms.util.Conversions;
import java.security.MessageDigest;
public class GiphyPaddedUrl implements Key {
private final String target;
private final long size;
public GiphyPaddedUrl(@NonNull String target, long size) {
this.target = target;
this.size = size;
}
public String getTarget() {
return target;
}
public long getSize() {
return size;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(target.getBytes());
messageDigest.update(Conversions.longToByteArray(size));
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof GiphyPaddedUrl)) return false;
GiphyPaddedUrl that = (GiphyPaddedUrl)other;
return this.target.equals(that.target) && this.size == that.size;
}
@Override
public int hashCode() {
return target.hashCode() ^ (int)size;
}
}

View File

@@ -24,12 +24,15 @@ import com.bumptech.glide.request.target.Target;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.giph.model.GiphyImage;
import org.thoughtcrime.securesms.giph.model.GiphyPaddedUrl;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -69,7 +72,7 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
Log.w(TAG, e);
synchronized (this) {
if (image.getGifUrl().equals(model)) {
if (new GiphyPaddedUrl(image.getGifUrl(), image.getGifSize()).equals(model)) {
this.modelReady = true;
notifyAll();
}
@@ -81,7 +84,7 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
synchronized (this) {
if (image.getGifUrl().equals(model)) {
if (new GiphyPaddedUrl(image.getGifUrl(), image.getGifSize()).equals(model)) {
this.modelReady = true;
notifyAll();
}
@@ -99,7 +102,8 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
}
return Glide.with(context)
.load(forMms ? image.getGifMmsUrl() : image.getGifUrl())
.load(forMms ? new GiphyPaddedUrl(image.getGifMmsUrl(), image.getMmsGifSize()) :
new GiphyPaddedUrl(image.getGifUrl(), image.getGifSize()))
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get();
}
@@ -144,18 +148,19 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
holder.gifProgress.setVisibility(View.GONE);
RequestBuilder<Drawable> thumbnailRequest = GlideApp.with(context)
.load(image.getStillUrl())
.load(new GiphyPaddedUrl(image.getStillUrl(), image.getStillSize()))
.diskCacheStrategy(DiskCacheStrategy.ALL);
if (Util.isLowMemory(context)) {
glideRequests.load(image.getStillUrl())
glideRequests.load(new GiphyPaddedUrl(image.getStillUrl(), image.getStillSize()))
.placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context)))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.listener(holder)
.into(holder.thumbnail);
holder.setModelReady();
} else {
glideRequests.load(image.getGifUrl())
glideRequests.load(new GiphyPaddedUrl(image.getGifUrl(), image.getGifSize()))
.thumbnail(thumbnailRequest)
.placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context)))
.diskCacheStrategy(DiskCacheStrategy.ALL)

View File

@@ -0,0 +1,285 @@
package org.thoughtcrime.securesms.glide;
import android.support.annotation.NonNull;
import android.util.Log;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.util.ContentLengthInputStream;
import org.thoughtcrime.securesms.giph.model.GiphyPaddedUrl;
import org.thoughtcrime.securesms.util.Util;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
class GiphyPaddedUrlFetcher implements DataFetcher<InputStream> {
private static final String TAG = GiphyPaddedUrlFetcher.class.getSimpleName();
private static final long MB = 1024 * 1024;
private static final long KB = 1024;
private final OkHttpClient client;
private final GiphyPaddedUrl url;
private List<ResponseBody> bodies;
private List<InputStream> rangeStreams;
private InputStream stream;
GiphyPaddedUrlFetcher(@NonNull OkHttpClient client,
@NonNull GiphyPaddedUrl url)
{
this.client = client;
this.url = url;
}
@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
bodies = new LinkedList<>();
rangeStreams = new LinkedList<>();
stream = null;
try {
List<ByteRange> requestPattern = getRequestPattern(url.getSize());
for (ByteRange range : requestPattern) {
Request request = new Request.Builder()
.addHeader("Range", "bytes=" + range.start + "-" + range.end)
.addHeader("Accept-Encoding", "identity")
.url(url.getTarget())
.get()
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Bad response: " + response.code() + " - " + response.message());
}
ResponseBody responseBody = response.body();
if (responseBody == null) throw new IOException("Response body was null");
else bodies.add(responseBody);
rangeStreams.add(new SkippingInputStream(ContentLengthInputStream.obtain(responseBody.byteStream(), responseBody.contentLength()), range.ignoreFirst));
}
stream = new InputStreamList(rangeStreams);
callback.onDataReady(stream);
} catch (IOException e) {
Log.w(TAG, e);
callback.onLoadFailed(e);
}
}
@Override
public void cleanup() {
if (rangeStreams != null) {
for (InputStream rangeStream : rangeStreams) {
try {
if (rangeStream != null) rangeStream.close();
} catch (IOException ignored) {}
}
}
if (bodies != null) {
for (ResponseBody body : bodies) {
if (body != null) body.close();
}
}
if (stream != null) {
try {
stream.close();
} catch (IOException ignored) {}
}
}
@Override
public void cancel() {
}
@NonNull
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
private List<ByteRange> getRequestPattern(long size) throws IOException {
if (size > MB) return getRequestPattern(size, MB);
else if (size > 500 * KB) return getRequestPattern(size, 500 * KB);
else if (size > 100 * KB) return getRequestPattern(size, 100 * KB);
else if (size > 50 * KB) return getRequestPattern(size, 50 * KB);
else if (size > KB) return getRequestPattern(size, KB);
throw new IOException("Unsupported size: " + size);
}
private List<ByteRange> getRequestPattern(long size, long increment) {
List<ByteRange> results = new LinkedList<>();
long offset = 0;
while (size - offset > increment) {
results.add(new ByteRange(offset, offset + increment - 1, 0));
offset += increment;
}
if (size - offset > 0) {
results.add(new ByteRange(size - increment, size-1, increment - (size - offset)));
}
return results;
}
private static class ByteRange {
private final long start;
private final long end;
private final long ignoreFirst;
private ByteRange(long start, long end, long ignoreFirst) {
this.start = start;
this.end = end;
this.ignoreFirst = ignoreFirst;
}
}
private static class SkippingInputStream extends FilterInputStream {
private long skip;
SkippingInputStream(InputStream in, long skip) {
super(in);
this.skip = skip;
}
@Override
public int read() throws IOException {
if (skip != 0) {
skipFully(skip);
skip = 0;
}
return super.read();
}
@Override
public int read(@NonNull byte[] buffer) throws IOException {
if (skip != 0) {
skipFully(skip);
skip = 0;
}
return super.read(buffer);
}
@Override
public int read(@NonNull byte[] buffer, int offset, int length) throws IOException {
if (skip != 0) {
skipFully(skip);
skip = 0;
}
return super.read(buffer, offset, length);
}
@Override
public int available() throws IOException {
return Util.toIntExact(super.available() - skip);
}
private void skipFully(long amount) throws IOException {
byte[] buffer = new byte[4096];
while (amount > 0) {
int read = super.read(buffer, 0, Math.min(buffer.length, Util.toIntExact(amount)));
if (read != -1) amount -= read;
else return;
}
}
}
private static class InputStreamList extends InputStream {
private final List<InputStream> inputStreams;
private int currentStreamIndex = 0;
InputStreamList(List<InputStream> inputStreams) {
this.inputStreams = inputStreams;
}
@Override
public int read() throws IOException {
while (currentStreamIndex < inputStreams.size()) {
int result = inputStreams.get(currentStreamIndex).read();
if (result == -1) currentStreamIndex++;
else return result;
}
return -1;
}
@Override
public int read(@NonNull byte[] buffer, int offset, int length) throws IOException {
while (currentStreamIndex < inputStreams.size()) {
int result = inputStreams.get(currentStreamIndex).read(buffer, offset, length);
if (result == -1) currentStreamIndex++;
else return result;
}
return -1;
}
@Override
public int read(@NonNull byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
@Override
public void close() throws IOException {
for (InputStream stream : inputStreams) {
try {
stream.close();
} catch (IOException ignored) {}
}
}
@Override
public int available() {
int total = 0;
for (int i=currentStreamIndex;i<inputStreams.size();i++) {
try {
int available = inputStreams.get(i).available();
if (available != -1) total += available;
} catch (IOException ignored) {}
}
return total;
}
}
}

View File

@@ -0,0 +1,52 @@
package org.thoughtcrime.securesms.glide;
import android.support.annotation.Nullable;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import org.thoughtcrime.securesms.giph.model.GiphyPaddedUrl;
import org.thoughtcrime.securesms.giph.net.GiphyProxySelector;
import java.io.InputStream;
import okhttp3.OkHttpClient;
public class GiphyPaddedUrlLoader implements ModelLoader<GiphyPaddedUrl, InputStream> {
private final OkHttpClient client;
private GiphyPaddedUrlLoader(OkHttpClient client) {
this.client = client;
}
@Nullable
@Override
public LoadData<InputStream> buildLoadData(GiphyPaddedUrl url, int width, int height, Options options) {
return new LoadData<>(url, new GiphyPaddedUrlFetcher(client, url));
}
@Override
public boolean handles(GiphyPaddedUrl url) {
return true;
}
public static class Factory implements ModelLoaderFactory<GiphyPaddedUrl, InputStream> {
private final OkHttpClient client;
public Factory() {
this.client = new OkHttpClient.Builder().proxySelector(new GiphyProxySelector()).build();
}
@Override
public ModelLoader<GiphyPaddedUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new GiphyPaddedUrlLoader(client);
}
@Override
public void teardown() {}
}
}

View File

@@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.InvalidMessageException;
@@ -165,8 +166,13 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
Log.w(TAG, "Downloading attachment with no digest...");
}
return new SignalServiceAttachmentPointer(id, null, key, relay, Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getFileName()), attachment.isVoiceNote());
} catch (InvalidMessageException | IOException e) {
return new SignalServiceAttachmentPointer(id, null, key, relay,
Optional.of(Util.toIntExact(attachment.getSize())),
Optional.absent(),
Optional.fromNullable(attachment.getDigest()),
Optional.fromNullable(attachment.getFileName()),
attachment.isVoiceNote());
} catch (InvalidMessageException | IOException | ArithmeticException e) {
Log.w(TAG, e);
throw new InvalidPartException(e);
}

View File

@@ -81,9 +81,9 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
attachment = File.createTempFile("avatar", "tmp", context.getCacheDir());
attachment.deleteOnExit();
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, relay, digest, fileName, false);
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, relay, Optional.of(0), Optional.absent(), digest, fileName, false);
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key), 500, 500);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);
database.updateAvatar(encodeId, avatar);
inputStream.close();

View File

@@ -608,7 +608,7 @@ public class PushDecryptJob extends ContextJob {
.getExpiringMessageManager()
.scheduleDeletion(messageId, true,
message.getExpirationStartTimestamp(),
message.getMessage().getExpiresInSeconds());
message.getMessage().getExpiresInSeconds() * 1000);
}
return threadId;

View File

@@ -19,19 +19,25 @@ class AttachmentStreamLocalUriFetcher implements DataFetcher<InputStream> {
private static final String TAG = AttachmentStreamLocalUriFetcher.class.getSimpleName();
private File attachment;
private byte[] key;
private final File attachment;
private final byte[] key;
private final Optional<byte[]> digest;
private final long plaintextLength;
private InputStream is;
AttachmentStreamLocalUriFetcher(File attachment, byte[] key) {
this.attachment = attachment;
this.key = key;
AttachmentStreamLocalUriFetcher(File attachment, long plaintextLength, byte[] key, Optional<byte[]> digest) {
this.attachment = attachment;
this.plaintextLength = plaintextLength;
this.digest = digest;
this.key = key;
}
@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
try {
is = new AttachmentCipherInputStream(attachment, key, Optional.absent());
if (!digest.isPresent()) throw new InvalidMessageException("No attachment digest!");
is = AttachmentCipherInputStream.createFor(attachment, plaintextLength, key, digest.get());
callback.onDataReady(is);
} catch (IOException | InvalidMessageException e) {
callback.onLoadFailed(e);

View File

@@ -10,6 +10,7 @@ import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.File;
import java.io.InputStream;
@@ -20,7 +21,7 @@ public class AttachmentStreamUriLoader implements ModelLoader<AttachmentModel, I
@Nullable
@Override
public LoadData<InputStream> buildLoadData(AttachmentModel attachmentModel, int width, int height, Options options) {
return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.key));
return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.plaintextLength, attachmentModel.key, attachmentModel.digest));
}
@Override
@@ -42,12 +43,18 @@ public class AttachmentStreamUriLoader implements ModelLoader<AttachmentModel, I
}
public static class AttachmentModel implements Key {
public @NonNull File attachment;
public @NonNull byte[] key;
public @NonNull File attachment;
public @NonNull byte[] key;
public @NonNull Optional<byte[]> digest;
public long plaintextLength;
public AttachmentModel(@NonNull File attachment, @NonNull byte[] key) {
this.attachment = attachment;
this.key = key;
public AttachmentModel(@NonNull File attachment, @NonNull byte[] key,
long plaintextLength, @NonNull Optional<byte[]> digest)
{
this.attachment = attachment;
this.key = key;
this.digest = digest;
this.plaintextLength = plaintextLength;
}
@Override

View File

@@ -13,7 +13,9 @@ import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.AppGlideModule;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.giph.model.GiphyPaddedUrl;
import org.thoughtcrime.securesms.glide.ContactPhotoLoader;
import org.thoughtcrime.securesms.glide.GiphyPaddedUrlLoader;
import org.thoughtcrime.securesms.glide.OkHttpUrlLoader;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
@@ -39,6 +41,7 @@ public class SignalGlideModule extends AppGlideModule {
registry.append(ContactPhoto.class, InputStream.class, new ContactPhotoLoader.Factory(context));
registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context));
registry.append(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
registry.append(GiphyPaddedUrl.class, InputStream.class, new GiphyPaddedUrlLoader.Factory());
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}