diff --git a/AndroidManifest.xml b/AndroidManifest.xml index bcc0dfd8f4..615d007ef5 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -125,10 +125,16 @@ android:launchMode="singleTask" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> - + + untrustedIdentities = new LinkedList<>(); List unregisteredUsers = new LinkedList<>(); + List networkExceptions = new LinkedList<>(); for (PushAddress recipient : recipients) { try { @@ -186,11 +189,14 @@ public class TextSecureMessageSender { } catch (UnregisteredUserException e) { Log.w(TAG, e); unregisteredUsers.add(e); + } catch (PushNetworkException e) { + Log.w(TAG, e); + networkExceptions.add(new NetworkFailureException(recipient.getNumber(), e)); } } - if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty()) { - throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers); + if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty() || !networkExceptions.isEmpty()) { + throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers, networkExceptions); } } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java index 56077f8635..663568a849 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java @@ -16,24 +16,28 @@ */ package org.whispersystems.textsecure.api.push; -import com.google.thoughtcrimegson.GsonBuilder; -import com.google.thoughtcrimegson.JsonDeserializationContext; -import com.google.thoughtcrimegson.JsonDeserializer; -import com.google.thoughtcrimegson.JsonElement; -import com.google.thoughtcrimegson.JsonParseException; -import com.google.thoughtcrimegson.JsonPrimitive; -import com.google.thoughtcrimegson.JsonSerializationContext; -import com.google.thoughtcrimegson.JsonSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.whispersystems.libaxolotl.ecc.ECPublicKey; -import org.whispersystems.textsecure.internal.util.Base64; import org.whispersystems.textsecure.internal.push.PreKeyEntity; +import org.whispersystems.textsecure.internal.util.Base64; import java.io.IOException; -import java.lang.reflect.Type; public class SignedPreKeyEntity extends PreKeyEntity { + @JsonProperty + @JsonSerialize(using = ByteArraySerializer.class) + @JsonDeserialize(using = ByteArrayDeserializer.class) private byte[] signature; public SignedPreKeyEntity() {} @@ -47,42 +51,18 @@ public class SignedPreKeyEntity extends PreKeyEntity { return signature; } - public static String toJson(SignedPreKeyEntity entity) { - GsonBuilder builder = new GsonBuilder(); - return forBuilder(builder).create().toJson(entity); - } - - public static SignedPreKeyEntity fromJson(String serialized) { - GsonBuilder builder = new GsonBuilder(); - return forBuilder(builder).create().fromJson(serialized, SignedPreKeyEntity.class); - } - - public static GsonBuilder forBuilder(GsonBuilder builder) { - return PreKeyEntity.forBuilder(builder) - .registerTypeAdapter(byte[].class, new ByteArrayJsonAdapter()); - - } - - private static class ByteArrayJsonAdapter - implements JsonSerializer, JsonDeserializer - { + private static class ByteArraySerializer extends JsonSerializer { @Override - public JsonElement serialize(byte[] signature, Type type, - JsonSerializationContext jsonSerializationContext) - { - return new JsonPrimitive(Base64.encodeBytesWithoutPadding(signature)); + public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytesWithoutPadding(value)); } + } + + private static class ByteArrayDeserializer extends JsonDeserializer { @Override - public byte[] deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext jsonDeserializationContext) - throws JsonParseException - { - try { - return Base64.decodeWithoutPadding(jsonElement.getAsJsonPrimitive().getAsString()); - } catch (IOException e) { - throw new JsonParseException(e); - } + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Base64.decodeWithoutPadding(p.getValueAsString()); } } } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java index 0617b59a59..dc0f15030b 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java @@ -24,12 +24,15 @@ public class EncapsulatedExceptions extends Throwable { private final List untrustedIdentityExceptions; private final List unregisteredUserExceptions; + private final List networkExceptions; public EncapsulatedExceptions(List untrustedIdentities, - List unregisteredUsers) + List unregisteredUsers, + List networkExceptions) { this.untrustedIdentityExceptions = untrustedIdentities; this.unregisteredUserExceptions = unregisteredUsers; + this.networkExceptions = networkExceptions; } public List getUntrustedIdentityExceptions() { @@ -39,4 +42,8 @@ public class EncapsulatedExceptions extends Throwable { public List getUnregisteredUserExceptions() { return unregisteredUserExceptions; } + + public List getNetworkExceptions() { + return networkExceptions; + } } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NetworkFailureException.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NetworkFailureException.java new file mode 100644 index 0000000000..bc9b42c5b3 --- /dev/null +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NetworkFailureException.java @@ -0,0 +1,15 @@ +package org.whispersystems.textsecure.api.push.exceptions; + +public class NetworkFailureException extends Exception { + + private final String e164number; + + public NetworkFailureException(String e164number, Exception nested) { + super(nested); + this.e164number = e164number; + } + + public String getE164number() { + return e164number; + } +} diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java index 0c7713c591..f875ba900d 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java @@ -19,6 +19,7 @@ package org.whispersystems.textsecure.api.push.exceptions; import java.io.IOException; public class PushNetworkException extends IOException { + public PushNetworkException(Exception exception) { super(exception); } @@ -26,4 +27,5 @@ public class PushNetworkException extends IOException { public PushNetworkException(String s) { super(s); } + } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java index 88e72d01b4..5d32b48378 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java @@ -16,10 +16,17 @@ */ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + public class AccountAttributes { + @JsonProperty private String signalingKey; + + @JsonProperty private boolean supportsSms; + + @JsonProperty private int registrationId; public AccountAttributes(String signalingKey, boolean supportsSms, int registrationId) { diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java index d2fc463ad8..fcc14df84e 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java @@ -1,7 +1,10 @@ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + public class DeviceCode { + @JsonProperty private String verificationCode; public String getVerificationCode() { diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java index 0f44b7c8c7..5a459a378b 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java @@ -16,11 +16,15 @@ */ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; public class MismatchedDevices { + @JsonProperty private List missingDevices; + @JsonProperty private List extraDevices; public List getMissingDevices() { diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java index 434de88ca9..845399a6df 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java @@ -17,14 +17,20 @@ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.whispersystems.textsecure.api.push.PushAddress; import org.whispersystems.textsecure.internal.util.Base64; public class OutgoingPushMessage { + @JsonProperty private int type; + @JsonProperty private int destinationDeviceId; + @JsonProperty private int destinationRegistrationId; + @JsonProperty private String body; public OutgoingPushMessage(PushAddress address, int deviceId, PushBody body) { diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java index 7adcb1ddc7..6fa26eab01 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java @@ -16,16 +16,22 @@ */ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; public class OutgoingPushMessageList { + @JsonProperty private String destination; + @JsonProperty private String relay; + @JsonProperty private long timestamp; + @JsonProperty private List messages; public OutgoingPushMessageList(String destination, long timestamp, String relay, diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java index ae67da9f38..7215a1eb5b 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java @@ -16,14 +16,15 @@ */ package org.whispersystems.textsecure.internal.push; -import com.google.thoughtcrimegson.GsonBuilder; -import com.google.thoughtcrimegson.JsonDeserializationContext; -import com.google.thoughtcrimegson.JsonDeserializer; -import com.google.thoughtcrimegson.JsonElement; -import com.google.thoughtcrimegson.JsonParseException; -import com.google.thoughtcrimegson.JsonPrimitive; -import com.google.thoughtcrimegson.JsonSerializationContext; -import com.google.thoughtcrimegson.JsonSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.ecc.Curve; @@ -31,11 +32,15 @@ import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.textsecure.internal.util.Base64; import java.io.IOException; -import java.lang.reflect.Type; public class PreKeyEntity { - private int keyId; + @JsonProperty + private int keyId; + + @JsonProperty + @JsonSerialize(using = ECPublicKeySerializer.class) + @JsonDeserialize(using = ECPublicKeyDeserializer.class) private ECPublicKey publicKey; public PreKeyEntity() {} @@ -53,32 +58,21 @@ public class PreKeyEntity { return publicKey; } - public static GsonBuilder forBuilder(GsonBuilder builder) { - return builder.registerTypeAdapter(ECPublicKey.class, new ECPublicKeyJsonAdapter()); + private static class ECPublicKeySerializer extends JsonSerializer { + @Override + public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); + } } - - private static class ECPublicKeyJsonAdapter - implements JsonSerializer, JsonDeserializer - { + private static class ECPublicKeyDeserializer extends JsonDeserializer { @Override - public JsonElement serialize(ECPublicKey preKeyPublic, Type type, - JsonSerializationContext jsonSerializationContext) - { - return new JsonPrimitive(Base64.encodeBytesWithoutPadding(preKeyPublic.serialize())); - } - - @Override - public ECPublicKey deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext jsonDeserializationContext) - throws JsonParseException - { + public ECPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { try { - return Curve.decodePoint(Base64.decodeWithoutPadding(jsonElement.getAsJsonPrimitive().getAsString()), 0); - } catch (InvalidKeyException | IOException e) { - throw new JsonParseException(e); + return Curve.decodePoint(Base64.decodeWithoutPadding(p.getValueAsString()), 0); + } catch (InvalidKeyException e) { + throw new IOException(e); } } } - } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java index 7b50d4ea82..1db05f80a1 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java @@ -16,26 +16,32 @@ */ package org.whispersystems.textsecure.internal.push; -import com.google.thoughtcrimegson.GsonBuilder; -import com.google.thoughtcrimegson.JsonDeserializationContext; -import com.google.thoughtcrimegson.JsonDeserializer; -import com.google.thoughtcrimegson.JsonElement; -import com.google.thoughtcrimegson.JsonParseException; -import com.google.thoughtcrimegson.JsonPrimitive; -import com.google.thoughtcrimegson.JsonSerializationContext; -import com.google.thoughtcrimegson.JsonSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.textsecure.internal.util.Base64; +import org.whispersystems.textsecure.internal.util.JsonUtil; import java.io.IOException; -import java.lang.reflect.Type; import java.util.List; public class PreKeyResponse { - private IdentityKey identityKey; + @JsonProperty + @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) + private IdentityKey identityKey; + + @JsonProperty private List devices; public IdentityKey getIdentityKey() { @@ -46,36 +52,5 @@ public class PreKeyResponse { return devices; } - public static PreKeyResponse fromJson(String serialized) { - GsonBuilder builder = new GsonBuilder(); - return PreKeyResponseItem.forBuilder(builder) - .registerTypeAdapter(IdentityKey.class, new IdentityKeyJsonAdapter()) - .create().fromJson(serialized, PreKeyResponse.class); - } - - public static class IdentityKeyJsonAdapter - implements JsonSerializer, JsonDeserializer - { - @Override - public JsonElement serialize(IdentityKey identityKey, Type type, - JsonSerializationContext jsonSerializationContext) - { - return new JsonPrimitive(Base64.encodeBytesWithoutPadding(identityKey.serialize())); - } - - @Override - public IdentityKey deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext jsonDeserializationContext) - throws JsonParseException - { - try { - return new IdentityKey(Base64.decodeWithoutPadding(jsonElement.getAsJsonPrimitive().getAsString()), 0); - } catch (InvalidKeyException | IOException e) { - throw new JsonParseException(e); - } - } - } - - } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java index 9400a2e093..6a5fe32ae0 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java @@ -16,15 +16,22 @@ */ package org.whispersystems.textsecure.internal.push; -import com.google.thoughtcrimegson.GsonBuilder; +import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.textsecure.api.push.SignedPreKeyEntity; public class PreKeyResponseItem { + @JsonProperty private int deviceId; + + @JsonProperty private int registrationId; + + @JsonProperty private SignedPreKeyEntity signedPreKey; + + @JsonProperty private PreKeyEntity preKey; public int getDeviceId() { @@ -43,7 +50,4 @@ public class PreKeyResponseItem { return preKey; } - public static GsonBuilder forBuilder(GsonBuilder builder) { - return SignedPreKeyEntity.forBuilder(builder); - } } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java index cb19cb9c95..d3d88a9aed 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java @@ -1,17 +1,29 @@ package org.whispersystems.textsecure.internal.push; -import com.google.thoughtcrimegson.GsonBuilder; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.textsecure.api.push.SignedPreKeyEntity; +import org.whispersystems.textsecure.internal.util.JsonUtil; import java.util.List; public class PreKeyState { + @JsonProperty + @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) private IdentityKey identityKey; + + @JsonProperty private List preKeys; + + @JsonProperty private PreKeyEntity lastResortKey; + + @JsonProperty private SignedPreKeyEntity signedPreKey; @@ -24,10 +36,4 @@ public class PreKeyState { this.identityKey = identityKey; } - public static String toJson(PreKeyState state) { - GsonBuilder builder = new GsonBuilder(); - return SignedPreKeyEntity.forBuilder(builder) - .registerTypeAdapter(IdentityKey.class, new PreKeyResponse.IdentityKeyJsonAdapter()) - .create().toJson(state); - } } diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java index 652f1ca417..cd53f71813 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java @@ -16,8 +16,11 @@ */ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + public class PreKeyStatus { + @JsonProperty private int count; public PreKeyStatus() {} diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java index b303e99415..942f788f10 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java @@ -18,8 +18,7 @@ package org.whispersystems.textsecure.internal.push; import android.util.Log; -import com.google.thoughtcrimegson.Gson; -import com.google.thoughtcrimegson.JsonParseException; +import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.http.conn.ssl.StrictHostnameVerifier; import org.whispersystems.libaxolotl.IdentityKey; @@ -44,6 +43,7 @@ import org.whispersystems.textsecure.internal.push.exceptions.MismatchedDevicesE import org.whispersystems.textsecure.internal.push.exceptions.StaleDevicesException; import org.whispersystems.textsecure.internal.util.Base64; import org.whispersystems.textsecure.internal.util.BlacklistingTrustManager; +import org.whispersystems.textsecure.internal.util.JsonUtil; import org.whispersystems.textsecure.internal.util.Util; import java.io.File; @@ -115,17 +115,17 @@ public class PushServiceSocket { { AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, supportsSms, registrationId); makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode), - "PUT", new Gson().toJson(signalingKeyEntity)); + "PUT", JsonUtil.toJson(signalingKeyEntity)); } public String getNewDeviceVerificationCode() throws IOException { String responseText = makeRequest(PROVISIONING_CODE_PATH, "GET", null); - return new Gson().fromJson(responseText, DeviceCode.class).getVerificationCode(); + return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode(); } public void sendProvisioningMessage(String destination, byte[] body) throws IOException { makeRequest(String.format(PROVISIONING_MESSAGE_PATH, destination), "PUT", - new Gson().toJson(new ProvisioningMessage(Base64.encodeBytes(body)))); + JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body)))); } public void sendReceipt(String destination, long messageId, String relay) throws IOException { @@ -140,7 +140,7 @@ public class PushServiceSocket { public void registerGcmId(String gcmRegistrationId) throws IOException { GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId, true); - makeRequest(REGISTER_GCM_PATH, "PUT", new Gson().toJson(registration)); + makeRequest(REGISTER_GCM_PATH, "PUT", JsonUtil.toJson(registration)); } public void unregisterGcmId() throws IOException { @@ -151,11 +151,10 @@ public class PushServiceSocket { throws IOException { try { - String responseText = makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle)); + String responseText = makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", JsonUtil.toJson(bundle)); if (responseText == null) return new SendMessageResponse(false); - else return new Gson().fromJson(responseText, SendMessageResponse.class); - + else return JsonUtil.fromJson(responseText, SendMessageResponse.class); } catch (NotFoundException nfe) { throw new UnregisteredUserException(bundle.getDestination(), nfe); } @@ -184,13 +183,13 @@ public class PushServiceSocket { signedPreKey.getSignature()); makeRequest(String.format(PREKEY_PATH, ""), "PUT", - PreKeyState.toJson(new PreKeyState(entities, lastResortEntity, - signedPreKeyEntity, identityKey))); + JsonUtil.toJson(new PreKeyState(entities, lastResortEntity, + signedPreKeyEntity, identityKey))); } public int getAvailablePreKeys() throws IOException { String responseText = makeRequest(PREKEY_METADATA_PATH, "GET", null); - PreKeyStatus preKeyStatus = new Gson().fromJson(responseText, PreKeyStatus.class); + PreKeyStatus preKeyStatus = JsonUtil.fromJson(responseText, PreKeyStatus.class); return preKeyStatus.getCount(); } @@ -209,7 +208,7 @@ public class PushServiceSocket { } String responseText = makeRequest(path, "GET", null); - PreKeyResponse response = PreKeyResponse.fromJson(responseText); + PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); List bundles = new LinkedList<>(); for (PreKeyResponseItem device : response.getDevices()) { @@ -236,7 +235,7 @@ public class PushServiceSocket { } return bundles; - } catch (JsonParseException e) { + } catch (JsonUtil.JsonParseException e) { throw new IOException(e); } catch (NotFoundException nfe) { throw new UnregisteredUserException(destination.getNumber(), nfe); @@ -253,7 +252,7 @@ public class PushServiceSocket { } String responseText = makeRequest(path, "GET", null); - PreKeyResponse response = PreKeyResponse.fromJson(responseText); + PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); if (response.getDevices() == null || response.getDevices().size() < 1) throw new IOException("Empty prekey list"); @@ -278,7 +277,7 @@ public class PushServiceSocket { return new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, response.getIdentityKey()); - } catch (JsonParseException e) { + } catch (JsonUtil.JsonParseException e) { throw new IOException(e); } catch (NotFoundException nfe) { throw new UnregisteredUserException(destination.getNumber(), nfe); @@ -288,7 +287,7 @@ public class PushServiceSocket { public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException { try { String responseText = makeRequest(SIGNED_PREKEY_PATH, "GET", null); - return SignedPreKeyEntity.fromJson(responseText); + return JsonUtil.fromJson(responseText, SignedPreKeyEntity.class); } catch (NotFoundException e) { Log.w("PushServiceSocket", e); return null; @@ -299,12 +298,12 @@ public class PushServiceSocket { SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), signedPreKey.getKeyPair().getPublicKey(), signedPreKey.getSignature()); - makeRequest(SIGNED_PREKEY_PATH, "PUT", SignedPreKeyEntity.toJson(signedPreKeyEntity)); + makeRequest(SIGNED_PREKEY_PATH, "PUT", JsonUtil.toJson(signedPreKeyEntity)); } public long sendAttachment(PushAttachmentData attachment) throws IOException { String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null); - AttachmentDescriptor attachmentKey = new Gson().fromJson(response, AttachmentDescriptor.class); + AttachmentDescriptor attachmentKey = JsonUtil.fromJson(response, AttachmentDescriptor.class); if (attachmentKey == null || attachmentKey.getLocation() == null) { throw new IOException("Server failed to allocate an attachment key!"); @@ -326,7 +325,7 @@ public class PushServiceSocket { } String response = makeRequest(path, "GET", null); - AttachmentDescriptor descriptor = new Gson().fromJson(response, AttachmentDescriptor.class); + AttachmentDescriptor descriptor = JsonUtil.fromJson(response, AttachmentDescriptor.class); Log.w("PushServiceSocket", "Attachment: " + attachmentId + " is at: " + descriptor.getLocation()); @@ -337,8 +336,8 @@ public class PushServiceSocket { throws NonSuccessfulResponseCodeException, PushNetworkException { ContactTokenList contactTokenList = new ContactTokenList(new LinkedList<>(contactTokens)); - String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", new Gson().toJson(contactTokenList)); - ContactTokenDetailsList activeTokens = new Gson().fromJson(response, ContactTokenDetailsList.class); + String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", JsonUtil.toJson(contactTokenList)); + ContactTokenDetailsList activeTokens = JsonUtil.fromJson(response, ContactTokenDetailsList.class); return activeTokens.getContacts(); } @@ -346,7 +345,7 @@ public class PushServiceSocket { public ContactTokenDetails getContactTokenDetails(String contactToken) throws IOException { try { String response = makeRequest(String.format(DIRECTORY_VERIFY_PATH, contactToken), "GET", null); - return new Gson().fromJson(response, ContactTokenDetails.class); + return JsonUtil.fromJson(response, ContactTokenDetails.class); } catch (NotFoundException nfe) { return null; } @@ -463,14 +462,14 @@ public class PushServiceSocket { } catch (IOException e) { throw new PushNetworkException(e); } - throw new MismatchedDevicesException(new Gson().fromJson(response, MismatchedDevices.class)); + throw new MismatchedDevicesException(JsonUtil.fromJson(response, MismatchedDevices.class)); case 410: try { response = Util.readFully(connection.getErrorStream()); } catch (IOException e) { throw new PushNetworkException(e); } - throw new StaleDevicesException(new Gson().fromJson(response, StaleDevices.class)); + throw new StaleDevicesException(JsonUtil.fromJson(response, StaleDevices.class)); case 417: throw new ExpectationFailedException(); } @@ -524,9 +523,7 @@ public class PushServiceSocket { return connection; } catch (IOException e) { throw new PushNetworkException(e); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (KeyManagementException e) { + } catch (NoSuchAlgorithmException | KeyManagementException e) { throw new AssertionError(e); } } @@ -540,7 +537,11 @@ public class PushServiceSocket { } private static class GcmRegistrationId { + + @JsonProperty private String gcmRegistrationId; + + @JsonProperty private boolean webSocketChannel; public GcmRegistrationId() {} @@ -552,7 +553,10 @@ public class PushServiceSocket { } private static class AttachmentDescriptor { + @JsonProperty private long id; + + @JsonProperty private String location; public long getId() { diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java index 34039dee40..6ea74ce2ee 100644 --- a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java @@ -16,10 +16,13 @@ */ package org.whispersystems.textsecure.internal.push; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; public class StaleDevices { + @JsonProperty private List staleDevices; public List getStaleDevices() { diff --git a/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/util/JsonUtil.java b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/util/JsonUtil.java new file mode 100644 index 0000000000..65e066f66b --- /dev/null +++ b/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/util/JsonUtil.java @@ -0,0 +1,75 @@ +package org.whispersystems.textsecure.internal.util; + +import android.util.Log; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; + +import java.io.IOException; + +public class JsonUtil { + + private static final String TAG = JsonUtil.class.getSimpleName(); + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public static String toJson(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + Log.w(TAG, e); + return ""; + } + } + + public static T fromJson(String json, Class clazz) { + try { + return objectMapper.readValue(json, clazz); + } catch (IOException e) { + Log.w(TAG, e); + throw new JsonParseException(e); + } + } + + public static class JsonParseException extends RuntimeException { + public JsonParseException(Exception e) { + super(e); + } + } + + public static class IdentityKeySerializer extends JsonSerializer { + @Override + public void serialize(IdentityKey value, JsonGenerator gen, SerializerProvider serializers) + throws IOException + { + gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); + } + } + + public static class IdentityKeyDeserializer extends JsonDeserializer { + @Override + public IdentityKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + return new IdentityKey(Base64.decodeWithoutPadding(p.getValueAsString()), 0); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + } + + +} diff --git a/res/drawable-xxhdpi/ic_error_red_24dp.png b/res/drawable-xxhdpi/ic_error_red_24dp.png new file mode 100644 index 0000000000..0a272ad41a Binary files /dev/null and b/res/drawable-xxhdpi/ic_error_red_24dp.png differ diff --git a/res/drawable-xxhdpi/ic_error_white_18dp.png b/res/drawable-xxhdpi/ic_error_white_18dp.png new file mode 100644 index 0000000000..036d5bed33 Binary files /dev/null and b/res/drawable-xxhdpi/ic_error_white_18dp.png differ diff --git a/res/drawable-xxhdpi/ic_info_outline_grey600_24dp.png b/res/drawable-xxhdpi/ic_info_outline_grey600_24dp.png new file mode 100644 index 0000000000..af621e2da1 Binary files /dev/null and b/res/drawable-xxhdpi/ic_info_outline_grey600_24dp.png differ diff --git a/res/drawable-xxhdpi/ic_refresh_white_18dp.png b/res/drawable-xxhdpi/ic_refresh_white_18dp.png new file mode 100644 index 0000000000..fe4285f679 Binary files /dev/null and b/res/drawable-xxhdpi/ic_refresh_white_18dp.png differ diff --git a/res/drawable/error_round.xml b/res/drawable/error_round.xml new file mode 100644 index 0000000000..56cc75291a --- /dev/null +++ b/res/drawable/error_round.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/drawable/info_round.xml b/res/drawable/info_round.xml new file mode 100644 index 0000000000..668eaf3bee --- /dev/null +++ b/res/drawable/info_round.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/conversation_item_received.xml b/res/layout/conversation_item_received.xml index 9705373dea..578436bfdf 100644 --- a/res/layout/conversation_item_received.xml +++ b/res/layout/conversation_item_received.xml @@ -47,7 +47,7 @@ @@ -111,7 +111,7 @@ + android:contentDescription="@string/conversation_item_sent__send_failed_indicator_description" /> + android:contentDescription="@string/conversation_item_sent__pending_approval_description" + tools:visibility="visible" /> + + android:textSize="16sp" + tools:text="Lorem ipsum mango dolor coconut papaya" /> diff --git a/res/layout/message_details_activity.xml b/res/layout/message_details_activity.xml new file mode 100644 index 0000000000..39603606f5 --- /dev/null +++ b/res/layout/message_details_activity.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/res/layout/message_details_header.xml b/res/layout/message_details_header.xml new file mode 100644 index 0000000000..c4ecaeea35 --- /dev/null +++ b/res/layout/message_details_header.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/message_details_recipient.xml b/res/layout/message_details_recipient.xml new file mode 100644 index 0000000000..c5c2648ccb --- /dev/null +++ b/res/layout/message_details_recipient.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + +