diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 46549e07ce..fe0ea8e29e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -97,6 +97,10 @@
android:allowBackup="false"
android:theme="@style/TextSecure.LightTheme">
+
+
diff --git a/build.gradle b/build.gradle
index f316abfed3..da041195fa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,7 +36,9 @@ repositories {
dependencies {
compile 'me.leolin:ShortcutBadger:1.1.0-WS1'
compile 'se.emilsjolander:stickylistheaders:2.7.0'
- compile 'com.google.android.gms:play-services-base:6.5.87'
+ compile 'com.google.android.gms:play-services-gcm:8.1.0'
+ compile 'com.google.android.gms:play-services-maps:8.1.0'
+ compile 'com.google.android.gms:play-services-location:8.1.0'
compile 'com.jpardogo.materialtabstrip:library:1.0.9'
compile 'org.w3c:smil:1.0.0'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
@@ -97,7 +99,9 @@ dependencyVerification {
verify = [
'me.leolin:ShortcutBadger:3142d017234bfa0cdd69ccded7cc5ea63f13b97574803c8c616c9bbeaad33ad9',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
- 'com.google.android.gms:play-services-base:832cb6b3130e871db6a412c4ab585656dbcc5e7948101f190186757785703f75',
+ 'com.google.android.gms:play-services-gcm:757ecd2c837ac81c98f4cc7dc783e7454c6d0506f6cc66b10417126b675248c9',
+ 'com.google.android.gms:play-services-maps:c58a9d98a98889fb0b27f78100f2d9341ed7722db24ccf832df62b6e8ce1b42e',
+ 'com.google.android.gms:play-services-location:8226f778aa86bd15b9143f62425262cc53d64021990f62eb1aaec108d4e25f35',
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
@@ -125,12 +129,14 @@ dependencyVerification {
'org.whispersystems:textsecure-android:aec5fc59952d9f5482491091091687816f46be1144342a0244f48fd55d6ab393',
'com.h6ah4i.android.compat:mulsellistprefcompat:47167c5cb796de1a854788e9ff318358e36c8fb88123baaa6e38fb78511dfabe',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
+ 'com.google.android.gms:play-services-base:ef36e50fa5c0415ed41f74dd399a889efd2fa327c449036e140c7c3786aa0e1f',
'com.android.support:support-annotations:104f353b53d5dd8d64b2f77eece4b37f6b961de9732eb6b706395e91033ec70a',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
'org.whispersystems:textsecure-java:b407ca6d1430204dfabf38e27db22d5177409072a9668238bd1877de7676ad3f',
'org.whispersystems:axolotl-android:40d3db5004a84749a73f68d2f0d01b2ae35a73c54df96d8c6c6723b96efb6fc0',
+ 'com.google.android.gms:play-services-basement:e1d29b21e02fd2a63e5a31807415cbb17a59568e27e3254181c01ffae10659bf',
'com.googlecode.libphonenumber:libphonenumber:9625de9d2270e9a280ff4e6d9ef3106573fb4828773fd32c9b7614f4e17d2811',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
'com.squareup.okhttp:okhttp:89b7f63e2e5b6c410266abc14f50fe52ea8d2d8a57260829e499b1cd9f0e61af',
diff --git a/res/drawable-hdpi/ic_location_on_white_36dp.png b/res/drawable-hdpi/ic_location_on_white_36dp.png
new file mode 100644
index 0000000000..b345cffca4
Binary files /dev/null and b/res/drawable-hdpi/ic_location_on_white_36dp.png differ
diff --git a/res/drawable-mdpi/ic_location_on_white_36dp.png b/res/drawable-mdpi/ic_location_on_white_36dp.png
new file mode 100644
index 0000000000..7c281c3f52
Binary files /dev/null and b/res/drawable-mdpi/ic_location_on_white_36dp.png differ
diff --git a/res/drawable-xhdpi/ic_location_on_white_36dp.png b/res/drawable-xhdpi/ic_location_on_white_36dp.png
new file mode 100644
index 0000000000..078b10d4fb
Binary files /dev/null and b/res/drawable-xhdpi/ic_location_on_white_36dp.png differ
diff --git a/res/drawable-xxhdpi/ic_location_on_white_36dp.png b/res/drawable-xxhdpi/ic_location_on_white_36dp.png
new file mode 100644
index 0000000000..633bc56957
Binary files /dev/null and b/res/drawable-xxhdpi/ic_location_on_white_36dp.png differ
diff --git a/res/drawable-xxxhdpi/ic_location_on_white_36dp.png b/res/drawable-xxxhdpi/ic_location_on_white_36dp.png
new file mode 100644
index 0000000000..42ab08cf74
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_location_on_white_36dp.png differ
diff --git a/res/layout/attachment_type_selector.xml b/res/layout/attachment_type_selector.xml
index a3780d8894..23df762566 100644
--- a/res/layout/attachment_type_selector.xml
+++ b/res/layout/attachment_type_selector.xml
@@ -143,6 +143,29 @@
android:gravity="center"
android:orientation="vertical">
+
+
+
+
+
+
+
+
diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index c43f3a788e..ca51f733cf 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -46,6 +46,13 @@
android:layout_height="wrap_content"
android:layout_gravity="center">
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 040df4bb7e..e76234b5a8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -617,6 +617,7 @@
Video
Contact
Camera
+ Location
Old passphrase
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index 0f7d7f77b5..8cddd77e63 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -56,6 +56,8 @@ 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;
import org.thoughtcrime.redphone.RedPhone;
@@ -174,6 +176,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int GROUP_EDIT = 5;
private static final int TAKE_PHOTO = 6;
private static final int ADD_CONTACT = 7;
+ private static final int PICK_LOCATION = 8;
private MasterSecret masterSecret;
protected ComposeText composeText;
@@ -312,7 +315,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
@Override
- public void onActivityResult(int reqCode, int resultCode, Intent data) {
+ public void onActivityResult(final int reqCode, int resultCode, Intent data) {
Log.w(TAG, "onActivityResult called: " + reqCode + ", " + resultCode + " , " + data);
super.onActivityResult(reqCode, resultCode, data);
@@ -349,6 +352,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipients.addListener(this);
fragment.reloadList();
break;
+ case PICK_LOCATION:
+ attachmentManager.setLocation(masterSecret, PlacePicker.getPlace(data, this), getCurrentMediaConstraints());
+ break;
}
}
@@ -998,6 +1004,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
AttachmentManager.selectAudio(this, PICK_AUDIO); break;
case AttachmentTypeSelectorAdapter.ADD_CONTACT_INFO:
AttachmentManager.selectContactInfo(this, PICK_CONTACT_INFO); break;
+ case AttachmentTypeSelector.ADD_LOCATION:
+ AttachmentManager.selectLocation(this, PICK_LOCATION); break;
case AttachmentTypeSelectorAdapter.TAKE_PHOTO:
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
}
diff --git a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
index 9415c5c48f..e37605d7a5 100644
--- a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
+++ b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
@@ -33,6 +33,7 @@ public class AttachmentTypeSelector extends PopupWindow {
public static final int ADD_SOUND = 3;
public static final int ADD_CONTACT_INFO = 4;
public static final int TAKE_PHOTO = 5;
+ public static final int ADD_LOCATION = 6;
private static final int ANIMATION_DURATION = 300;
@@ -43,6 +44,7 @@ public class AttachmentTypeSelector extends PopupWindow {
private final @NonNull ImageView videoButton;
private final @NonNull ImageView contactButton;
private final @NonNull ImageView cameraButton;
+ private final @NonNull ImageView locationButton;
private final @NonNull ImageView closeButton;
private @Nullable View currentAnchor;
@@ -54,21 +56,27 @@ public class AttachmentTypeSelector extends PopupWindow {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.attachment_type_selector, null, true);
- this.listener = listener;
- this.imageButton = ViewUtil.findById(layout, R.id.gallery_button);
- this.audioButton = ViewUtil.findById(layout, R.id.audio_button);
- this.videoButton = ViewUtil.findById(layout, R.id.video_button);
- this.contactButton = ViewUtil.findById(layout, R.id.contact_button);
- this.cameraButton = ViewUtil.findById(layout, R.id.camera_button);
- this.closeButton = ViewUtil.findById(layout, R.id.close_button);
+ this.listener = listener;
+ this.imageButton = ViewUtil.findById(layout, R.id.gallery_button);
+ this.audioButton = ViewUtil.findById(layout, R.id.audio_button);
+ this.videoButton = ViewUtil.findById(layout, R.id.video_button);
+ this.contactButton = ViewUtil.findById(layout, R.id.contact_button);
+ this.cameraButton = ViewUtil.findById(layout, R.id.camera_button);
+ this.closeButton = ViewUtil.findById(layout, R.id.close_button);
+ this.locationButton = ViewUtil.findById(layout, R.id.location_button);
this.imageButton.setOnClickListener(new PropagatingClickListener(ADD_IMAGE));
this.audioButton.setOnClickListener(new PropagatingClickListener(ADD_SOUND));
this.videoButton.setOnClickListener(new PropagatingClickListener(ADD_VIDEO));
this.contactButton.setOnClickListener(new PropagatingClickListener(ADD_CONTACT_INFO));
this.cameraButton.setOnClickListener(new PropagatingClickListener(TAKE_PHOTO));
+ this.locationButton.setOnClickListener(new PropagatingClickListener(ADD_LOCATION));
this.closeButton.setOnClickListener(new CloseClickListener());
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ this.locationButton.setVisibility(View.INVISIBLE);
+ }
+
setContentView(layout);
setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
diff --git a/src/org/thoughtcrime/securesms/components/location/SignalMapView.java b/src/org/thoughtcrime/securesms/components/location/SignalMapView.java
new file mode 100644
index 0000000000..067af7bb85
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/location/SignalMapView.java
@@ -0,0 +1,98 @@
+package org.thoughtcrime.securesms.components.location;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.google.android.gms.location.places.Place;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.MapView;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.util.ViewUtil;
+import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
+import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
+
+public class SignalMapView extends LinearLayout {
+
+ private MapView mapView;
+ private ImageView imageView;
+ private TextView textView;
+
+ public SignalMapView(Context context) {
+ this(context, null);
+ }
+
+ public SignalMapView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize(context);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public SignalMapView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initialize(context);
+ }
+
+ private void initialize(Context context) {
+ setOrientation(LinearLayout.VERTICAL);
+ LayoutInflater.from(context).inflate(R.layout.signal_map_view, this, true);
+
+ this.mapView = ViewUtil.findById(this, R.id.map_view);
+ this.imageView = ViewUtil.findById(this, R.id.image_view);
+ this.textView = ViewUtil.findById(this, R.id.address_view);
+ }
+
+ public ListenableFuture display(final SignalPlace place) {
+ final SettableFuture future = new SettableFuture<>();
+
+ this.mapView.onCreate(null);
+ this.mapView.onResume();
+
+ this.mapView.setVisibility(View.VISIBLE);
+ this.imageView.setVisibility(View.GONE);
+
+ this.mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(final GoogleMap googleMap) {
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(place.getLatLong(), 13));
+ googleMap.addMarker(new MarkerOptions().position(place.getLatLong()));
+ googleMap.setBuildingsEnabled(true);
+ googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
+ googleMap.getUiSettings().setAllGesturesEnabled(false);
+ googleMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
+ @Override
+ public void onMapLoaded() {
+ googleMap.snapshot(new GoogleMap.SnapshotReadyCallback() {
+ @Override
+ public void onSnapshotReady(Bitmap bitmap) {
+ future.set(bitmap);
+ imageView.setImageBitmap(bitmap);
+ imageView.setVisibility(View.VISIBLE);
+ mapView.setVisibility(View.GONE);
+ mapView.onPause();
+ mapView.onDestroy();
+ }
+ });
+ }
+ });
+ }
+ });
+
+ this.textView.setText(place.getDescription());
+
+ return future;
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/components/location/SignalPlace.java b/src/org/thoughtcrime/securesms/components/location/SignalPlace.java
new file mode 100644
index 0000000000..c83a3c8bd7
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/location/SignalPlace.java
@@ -0,0 +1,37 @@
+package org.thoughtcrime.securesms.components.location;
+
+import android.text.TextUtils;
+
+import com.google.android.gms.location.places.Place;
+import com.google.android.gms.maps.model.LatLng;
+
+public class SignalPlace {
+
+ private static final String URL = "https://maps.google.com/maps?q=%s,%s";
+
+ private final Place place;
+
+ public SignalPlace(Place place) {
+ this.place = place;
+ }
+
+ public LatLng getLatLong() {
+ return place.getLatLng();
+ }
+
+ public String getDescription() {
+ String description = "";
+
+ if (!TextUtils.isEmpty(place.getName())) {
+ description += (place.getName() + "\n");
+ }
+
+ if (!TextUtils.isEmpty(place.getAddress())) {
+ description += (place.getAddress() + "\n");
+ }
+
+ description += String.format(URL, place.getLatLng().latitude, place.getLatLng().longitude);
+
+ return description;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
index a75ac3eb40..55a89a3a34 100644
--- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -32,15 +33,25 @@ import android.util.Log;
import android.view.View;
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.SignalPlace;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
+import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
+import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
+import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
import org.whispersystems.libaxolotl.util.guava.Optional;
@@ -61,6 +72,7 @@ public class AttachmentManager {
private final @NonNull RemovableMediaView removableMediaView;
private final @NonNull ThumbnailView thumbnail;
private final @NonNull AudioView audioView;
+ private final @NonNull SignalMapView mapView;
private final @NonNull AttachmentListener attachmentListener;
private @NonNull List garbage = new LinkedList<>();
@@ -71,6 +83,7 @@ public class AttachmentManager {
this.attachmentView = ViewUtil.findById(activity, R.id.attachment_editor);
this.thumbnail = ViewUtil.findById(activity, R.id.attachment_thumbnail);
this.audioView = ViewUtil.findById(activity, R.id.attachment_audio);
+ this.mapView = ViewUtil.findById(activity, R.id.attachment_location);
this.removableMediaView = ViewUtil.findById(activity, R.id.removable_media_view);
this.context = activity;
this.attachmentListener = listener;
@@ -134,6 +147,29 @@ public class AttachmentManager {
this.slide = Optional.of(slide);
}
+ public void setLocation(@NonNull final MasterSecret masterSecret,
+ @NonNull final Place place,
+ @NonNull final MediaConstraints constraints)
+ {
+ final SignalPlace signalPlace = new SignalPlace(place);
+ ListenableFuture future = mapView.display(signalPlace);
+
+ attachmentView.setVisibility(View.VISIBLE);
+ removableMediaView.display(mapView);
+
+ future.addListener(new AssertedSuccessListener() {
+ @Override
+ 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());
+
+ setSlide(locationSlide);
+ attachmentListener.onAttachmentChanged();
+ }
+ });
+ }
+
public void setMedia(@NonNull final MasterSecret masterSecret,
@NonNull final Uri uri,
@NonNull final MediaType mediaType,
@@ -218,6 +254,14 @@ public class AttachmentManager {
activity.startActivityForResult(intent, requestCode);
}
+ public static void selectLocation(Activity activity, int requestCode) {
+ try {
+ activity.startActivityForResult(new PlacePicker.IntentBuilder().build(activity), requestCode);
+ } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) {
+ Log.w(TAG, e);
+ }
+ }
+
private @Nullable Uri getSlideUri() {
return slide.isPresent() ? slide.get().getUri() : null;
}
@@ -310,7 +354,6 @@ public class AttachmentManager {
public @NonNull Slide createSlide(@NonNull Context context,
@NonNull Uri uri,
long dataSize)
- throws IOException
{
switch (this) {
case IMAGE: return new ImageSlide(context, uri, dataSize);
diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
index ab76fee39f..46cb231079 100644
--- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
@@ -36,7 +36,7 @@ import ws.com.google.android.mms.pdu.PduPart;
public class AudioSlide extends Slide {
- public AudioSlide(Context context, Uri uri, long dataSize) throws IOException {
+ public AudioSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, ContentType.AUDIO_UNSPECIFIED, dataSize));
}
diff --git a/src/org/thoughtcrime/securesms/mms/GifSlide.java b/src/org/thoughtcrime/securesms/mms/GifSlide.java
index 22b4b410da..614e5ad557 100644
--- a/src/org/thoughtcrime/securesms/mms/GifSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/GifSlide.java
@@ -19,7 +19,7 @@ public class GifSlide extends ImageSlide {
super(context, attachment);
}
- public GifSlide(Context context, Uri uri, long size) throws IOException {
+ public GifSlide(Context context, Uri uri, long size) {
super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_GIF, size));
}
diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
index e7d8eb46a1..2971086f39 100644
--- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
@@ -37,7 +37,7 @@ public class ImageSlide extends Slide {
super(context, attachment);
}
- public ImageSlide(Context context, Uri uri, long size) throws IOException {
+ public ImageSlide(Context context, Uri uri, long size) {
super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_JPEG, size));
}
diff --git a/src/org/thoughtcrime/securesms/mms/LocationSlide.java b/src/org/thoughtcrime/securesms/mms/LocationSlide.java
new file mode 100644
index 0000000000..fc6b113f02
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/mms/LocationSlide.java
@@ -0,0 +1,25 @@
+package org.thoughtcrime.securesms.mms;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import org.whispersystems.libaxolotl.util.guava.Optional;
+
+public class LocationSlide extends ImageSlide {
+
+ @NonNull
+ private final String description;
+
+ public LocationSlide(@NonNull Context context, @NonNull Uri uri, long size, @NonNull String description)
+ {
+ super(context, uri, size);
+ this.description = description;
+ }
+
+ @Override
+ @NonNull
+ public Optional getBody() {
+ return Optional.of(description);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java
index c10850e662..af07384638 100644
--- a/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java
+++ b/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java
@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.mms;
+import android.text.TextUtils;
+
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.recipients.Recipients;
@@ -26,7 +28,11 @@ public class OutgoingMediaMessage {
public OutgoingMediaMessage(Recipients recipients, SlideDeck slideDeck, String message, long sentTimeMillis, int distributionType)
{
- this(recipients, message, slideDeck.asAttachments(), sentTimeMillis, distributionType);
+ this(recipients,
+ TextUtils.isEmpty(message) ? slideDeck.getBody() : slideDeck.getBody() + "\n\n" + message,
+ slideDeck.asAttachments(),
+ sentTimeMillis,
+ distributionType);
}
public OutgoingMediaMessage(OutgoingMediaMessage that) {
diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java
index f68e8b4b3d..c0bf65105d 100644
--- a/src/org/thoughtcrime/securesms/mms/Slide.java
+++ b/src/org/thoughtcrime/securesms/mms/Slide.java
@@ -30,8 +30,6 @@ import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.util.guava.Optional;
-import java.io.IOException;
-
public abstract class Slide {
protected final Attachment attachment;
@@ -57,6 +55,11 @@ public abstract class Slide {
return attachment.getThumbnailUri();
}
+ @NonNull
+ public Optional getBody() {
+ return Optional.absent();
+ }
+
public boolean hasImage() {
return false;
}
@@ -100,7 +103,6 @@ public abstract class Slide {
@NonNull Uri uri,
@NonNull String defaultMime,
long size)
- throws IOException
{
Optional resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri));
return new UriAttachment(uri, resolvedType.or(defaultMime), AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size);
diff --git a/src/org/thoughtcrime/securesms/mms/SlideDeck.java b/src/org/thoughtcrime/securesms/mms/SlideDeck.java
index 8709494587..65bfefb2df 100644
--- a/src/org/thoughtcrime/securesms/mms/SlideDeck.java
+++ b/src/org/thoughtcrime/securesms/mms/SlideDeck.java
@@ -17,10 +17,12 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.util.MediaUtil;
+import org.whispersystems.libaxolotl.util.guava.Optional;
import java.util.LinkedList;
import java.util.List;
@@ -48,6 +50,22 @@ public class SlideDeck {
slides.clear();
}
+ @NonNull
+ public String getBody() {
+ String body = "";
+
+ for (Slide slide : slides) {
+ Optional slideBody = slide.getBody();
+
+ if (slideBody.isPresent()) {
+ body = slideBody.get();
+ }
+ }
+
+ return body;
+ }
+
+ @NonNull
public List asAttachments() {
List attachments = new LinkedList<>();
diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
index 63be2dcc79..8c2000b42e 100644
--- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
@@ -34,7 +34,7 @@ import ws.com.google.android.mms.pdu.PduPart;
public class VideoSlide extends Slide {
- public VideoSlide(Context context, Uri uri, long dataSize) throws IOException {
+ public VideoSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize));
}