diff --git a/res/values/strings.xml b/res/values/strings.xml
index e76234b5a..73c773688 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -51,6 +51,7 @@
(image)
(audio)
(video)
+ (location)
Can\'t find an app to select media.
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index 8cddd77e6..60eb72929 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -56,7 +56,6 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
-import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlacePicker;
import com.google.protobuf.ByteString;
@@ -78,6 +77,7 @@ import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer;
import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.AttachmentDrawerListener;
import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.DrawerState;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
+import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.thoughtcrime.securesms.components.reminder.InviteReminder;
import org.thoughtcrime.securesms.components.reminder.ReminderView;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
@@ -97,6 +97,7 @@ import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter;
import org.thoughtcrime.securesms.mms.AudioSlide;
+import org.thoughtcrime.securesms.mms.LocationSlide;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@@ -353,7 +354,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
fragment.reloadList();
break;
case PICK_LOCATION:
- attachmentManager.setLocation(masterSecret, PlacePicker.getPlace(data, this), getCurrentMediaConstraints());
+ SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
+ attachmentManager.setLocation(masterSecret, place, getCurrentMediaConstraints());
break;
}
}
@@ -748,14 +750,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onPostExecute(List drafts) {
for (Draft draft : drafts) {
- if (draft.getType().equals(Draft.TEXT)) {
- composeText.setText(draft.getValue());
- } else if (draft.getType().equals(Draft.IMAGE)) {
- setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE);
- } else if (draft.getType().equals(Draft.AUDIO)) {
- setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO);
- } else if (draft.getType().equals(Draft.VIDEO)) {
- setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO);
+ try {
+ if (draft.getType().equals(Draft.TEXT)) {
+ composeText.setText(draft.getValue());
+ } else if (draft.getType().equals(Draft.LOCATION)) {
+ attachmentManager.setLocation(masterSecret, SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints());
+ } else if (draft.getType().equals(Draft.IMAGE)) {
+ setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE);
+ } else if (draft.getType().equals(Draft.AUDIO)) {
+ setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO);
+ } else if (draft.getType().equals(Draft.VIDEO)) {
+ setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO);
+ }
+ } catch (IOException e) {
+ Log.w(TAG, e);
}
}
@@ -1053,9 +1061,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) {
- if (slide.hasAudio()) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
- else if (slide.hasVideo()) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
- else if (slide.hasImage()) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
+ if (slide.hasAudio()) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
+ else if (slide.hasVideo()) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
+ else if (slide.hasLocation()) drafts.add(new Draft(Draft.LOCATION, ((LocationSlide)slide).getPlace().serialize()));
+ else if (slide.hasImage()) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
}
return drafts;
diff --git a/src/org/thoughtcrime/securesms/components/location/SignalPlace.java b/src/org/thoughtcrime/securesms/components/location/SignalPlace.java
index c83a3c8bd..64dcc4870 100644
--- a/src/org/thoughtcrime/securesms/components/location/SignalPlace.java
+++ b/src/org/thoughtcrime/securesms/components/location/SignalPlace.java
@@ -1,37 +1,77 @@
package org.thoughtcrime.securesms.components.location;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
+import android.util.Log;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.maps.model.LatLng;
+import org.thoughtcrime.securesms.util.JsonUtils;
+
+import java.io.IOException;
+
public class SignalPlace {
private static final String URL = "https://maps.google.com/maps?q=%s,%s";
+ private static final String TAG = SignalPlace.class.getSimpleName();
- private final Place place;
+ @JsonProperty
+ private String name;
+
+ @JsonProperty
+ private String address;
+
+ @JsonProperty
+ private double latitude;
+
+ @JsonProperty
+ private double longitude;
public SignalPlace(Place place) {
- this.place = place;
+ this.name = place.getName().toString();
+ this.address = place.getAddress().toString();
+ this.latitude = place.getLatLng().latitude;
+ this.longitude = place.getLatLng().longitude;
}
+ public SignalPlace() {}
+
+ @JsonIgnore
public LatLng getLatLong() {
- return place.getLatLng();
+ return new LatLng(latitude, longitude);
}
+ @JsonIgnore
public String getDescription() {
String description = "";
- if (!TextUtils.isEmpty(place.getName())) {
- description += (place.getName() + "\n");
+ if (!TextUtils.isEmpty(name)) {
+ description += (name + "\n");
}
- if (!TextUtils.isEmpty(place.getAddress())) {
- description += (place.getAddress() + "\n");
+ if (!TextUtils.isEmpty(address)) {
+ description += (address + "\n");
}
- description += String.format(URL, place.getLatLng().latitude, place.getLatLng().longitude);
+ description += String.format(URL, latitude, longitude);
return description;
}
+
+ public @Nullable String serialize() {
+ try {
+ return JsonUtils.toJson(this);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
+ }
+ }
+
+ public static SignalPlace deserialize(@NonNull String serialized) throws IOException {
+ return JsonUtils.fromJson(serialized, SignalPlace.class);
+ }
}
diff --git a/src/org/thoughtcrime/securesms/crypto/MasterCipher.java b/src/org/thoughtcrime/securesms/crypto/MasterCipher.java
index cdc6263c8..969320391 100644
--- a/src/org/thoughtcrime/securesms/crypto/MasterCipher.java
+++ b/src/org/thoughtcrime/securesms/crypto/MasterCipher.java
@@ -75,7 +75,7 @@ public class MasterCipher {
return encryptBytes(privateKey.serialize());
}
- public String encryptBody(String body) {
+ public String encryptBody(@NonNull String body) {
return encryptAndEncodeBytes(body.getBytes());
}
@@ -149,7 +149,7 @@ public class MasterCipher {
}
}
- private String encryptAndEncodeBytes(byte[] bytes) {
+ private String encryptAndEncodeBytes(@NonNull byte[] bytes) {
byte[] encryptedAndMacBody = encryptBytes(bytes);
return Base64.encodeBytes(encryptedAndMacBody);
}
diff --git a/src/org/thoughtcrime/securesms/database/DraftDatabase.java b/src/org/thoughtcrime/securesms/database/DraftDatabase.java
index b9a5ceab8..30470de9b 100644
--- a/src/org/thoughtcrime/securesms/database/DraftDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/DraftDatabase.java
@@ -103,10 +103,11 @@ public class DraftDatabase extends Database {
}
public static class Draft {
- public static final String TEXT = "text";
- public static final String IMAGE = "image";
- public static final String VIDEO = "video";
- public static final String AUDIO = "audio";
+ public static final String TEXT = "text";
+ public static final String IMAGE = "image";
+ public static final String VIDEO = "video";
+ public static final String AUDIO = "audio";
+ public static final String LOCATION = "location";
private final String type;
private final String value;
@@ -126,11 +127,12 @@ public class DraftDatabase extends Database {
public String getSnippet(Context context) {
switch (type) {
- case TEXT: return value;
- case IMAGE: return context.getString(R.string.DraftDatabase_Draft_image_snippet);
- case VIDEO: return context.getString(R.string.DraftDatabase_Draft_video_snippet);
- case AUDIO: return context.getString(R.string.DraftDatabase_Draft_audio_snippet);
- default: return null;
+ case TEXT: return value;
+ case IMAGE: return context.getString(R.string.DraftDatabase_Draft_image_snippet);
+ case VIDEO: return context.getString(R.string.DraftDatabase_Draft_video_snippet);
+ case AUDIO: return context.getString(R.string.DraftDatabase_Draft_audio_snippet);
+ case LOCATION: return context.getString(R.string.DraftDatabase_Draft_location_snippet);
+ default: return null;
}
}
}
diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
index 55a89a3a3..86f3df9e1 100644
--- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -35,15 +35,14 @@ import android.widget.Toast;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
-import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlacePicker;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.components.RemovableMediaView;
-import org.thoughtcrime.securesms.components.location.SignalMapView;
import org.thoughtcrime.securesms.components.ThumbnailView;
+import org.thoughtcrime.securesms.components.location.SignalMapView;
import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
@@ -148,11 +147,10 @@ public class AttachmentManager {
}
public void setLocation(@NonNull final MasterSecret masterSecret,
- @NonNull final Place place,
+ @NonNull final SignalPlace place,
@NonNull final MediaConstraints constraints)
{
- final SignalPlace signalPlace = new SignalPlace(place);
- ListenableFuture future = mapView.display(signalPlace);
+ ListenableFuture future = mapView.display(place);
attachmentView.setVisibility(View.VISIBLE);
removableMediaView.display(mapView);
@@ -162,7 +160,7 @@ public class AttachmentManager {
public void onSuccess(@NonNull Bitmap result) {
byte[] blob = BitmapUtil.toByteArray(result);
Uri uri = PersistentBlobProvider.getInstance(context).create(masterSecret, blob);
- LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, signalPlace.getDescription());
+ LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place);
setSlide(locationSlide);
attachmentListener.onAttachmentChanged();
diff --git a/src/org/thoughtcrime/securesms/mms/LocationSlide.java b/src/org/thoughtcrime/securesms/mms/LocationSlide.java
index fc6b113f0..e9e6b9a14 100644
--- a/src/org/thoughtcrime/securesms/mms/LocationSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/LocationSlide.java
@@ -4,22 +4,34 @@ import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
+import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.whispersystems.libaxolotl.util.guava.Optional;
public class LocationSlide extends ImageSlide {
@NonNull
- private final String description;
+ private final SignalPlace place;
- public LocationSlide(@NonNull Context context, @NonNull Uri uri, long size, @NonNull String description)
+ public LocationSlide(@NonNull Context context, @NonNull Uri uri, long size, @NonNull SignalPlace place)
{
super(context, uri, size);
- this.description = description;
+ this.place = place;
}
@Override
@NonNull
public Optional getBody() {
- return Optional.of(description);
+ return Optional.of(place.getDescription());
}
+
+ @NonNull
+ public SignalPlace getPlace() {
+ return place;
+ }
+
+ @Override
+ public boolean hasLocation() {
+ return true;
+ }
+
}
diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java
index c0bf65105..f676ac3a6 100644
--- a/src/org/thoughtcrime/securesms/mms/Slide.java
+++ b/src/org/thoughtcrime/securesms/mms/Slide.java
@@ -72,6 +72,10 @@ public abstract class Slide {
return false;
}
+ public boolean hasLocation() {
+ return false;
+ }
+
public @NonNull String getContentDescription() { return ""; }
public Attachment asAttachment() {