allow sharing multiple images at once. fixes #1090

iNPUTmice created

Change summary

src/main/AndroidManifest.xml                                      |   7 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java | 109 
src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java    |  54 
3 files changed, 111 insertions(+), 59 deletions(-)

Detailed changes

src/main/AndroidManifest.xml 🔗

@@ -122,6 +122,13 @@
 
                 <data android:mimeType="*/*" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND_MULTIPLE" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+
+                <data android:mimeType="image/*" />
+            </intent-filter>
         </activity>
         <activity
             android:name="de.duenndns.ssl.MemorizingActivity"

src/main/java/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -5,6 +5,7 @@ import android.app.ActionBar;
 import android.app.AlertDialog;
 import android.app.FragmentTransaction;
 import android.app.PendingIntent;
+import android.content.ClipData;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
@@ -30,6 +31,7 @@ import net.java.otr4j.session.SessionStatus;
 import de.timroes.android.listview.EnhancedListView;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import eu.siacs.conversations.R;
@@ -69,8 +71,8 @@ public class ConversationActivity extends XmppActivity
 
 	private String mOpenConverstaion = null;
 	private boolean mPanelOpen = true;
-	private Uri mPendingImageUri = null;
-	private Uri mPendingFileUri = null;
+	final private List<Uri> mPendingImageUris = new ArrayList<>();
+	final private List<Uri> mPendingFileUris = new ArrayList<>();
 	private Uri mPendingGeoUri = null;
 
 	private View mContentView;
@@ -141,13 +143,14 @@ public class ConversationActivity extends XmppActivity
 	@Override
 	protected void onCreate(final Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
-		if (savedInstanceState != null) {mOpenConverstaion = savedInstanceState.getString(
-				STATE_OPEN_CONVERSATION, null);
-		mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
-		String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
-		if (pending != null) {
-			mPendingImageUri = Uri.parse(pending);
-		}
+		if (savedInstanceState != null) {
+			mOpenConverstaion = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
+			mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
+			String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
+			if (pending != null) {
+				mPendingImageUris.clear();
+				mPendingImageUris.add(Uri.parse(pending));
+			}
 		}
 
 		setContentView(R.layout.fragment_conversations_overview);
@@ -409,13 +412,18 @@ public class ConversationActivity extends XmppActivity
 					switch (attachmentChoice) {
 						case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
 							intent.setAction(Intent.ACTION_GET_CONTENT);
+							if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+								intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
+							}
 							intent.setType("image/*");
 							chooser = true;
 							break;
 						case ATTACHMENT_CHOICE_TAKE_PHOTO:
-							mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+							Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
 							intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
-							intent.putExtra(MediaStore.EXTRA_OUTPUT, mPendingImageUri);
+							intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+							mPendingImageUris.clear();
+							mPendingImageUris.add(uri);
 							break;
 						case ATTACHMENT_CHOICE_CHOOSE_FILE:
 							chooser = true;
@@ -857,8 +865,8 @@ public class ConversationActivity extends XmppActivity
 		}
 		savedInstanceState.putBoolean(STATE_PANEL_OPEN,
 				isConversationsOverviewVisable());
-		if (this.mPendingImageUri != null) {
-			savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUri.toString());
+		if (this.mPendingImageUris.size() >= 1) {
+			savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString());
 		}
 		super.onSaveInstanceState(savedInstanceState);
 	}
@@ -897,21 +905,23 @@ public class ConversationActivity extends XmppActivity
 			this.mConversationFragment.reInit(getSelectedConversation());
 		} else {
 			showConversationsOverview();
-			mPendingImageUri = null;
-			mPendingFileUri = null;
+			mPendingImageUris.clear();
+			mPendingFileUris.clear();
 			mPendingGeoUri = null;
 			setSelectedConversation(conversationList.get(0));
 			this.mConversationFragment.reInit(getSelectedConversation());
 		}
 
-		if (mPendingImageUri != null) {
-			attachImageToConversation(getSelectedConversation(),mPendingImageUri);
-			mPendingImageUri = null;
-		} else if (mPendingFileUri != null) {
-			attachFileToConversation(getSelectedConversation(),mPendingFileUri);
-			mPendingFileUri = null;
-		} else if (mPendingGeoUri != null) {
-			attachLocationToConversation(getSelectedConversation(),mPendingGeoUri);
+		for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
+			attachImageToConversation(getSelectedConversation(),i.next());
+		}
+
+		for(Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+			attachFileToConversation(getSelectedConversation(),i.next());
+		}
+
+		if (mPendingGeoUri != null) {
+			attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
 			mPendingGeoUri = null;
 		}
 		ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
@@ -963,6 +973,20 @@ public class ConversationActivity extends XmppActivity
 		xmppConnectionService.getNotificationService().setOpenConversation(null);
 	}
 
+	private static List<Uri> extractUriFromIntent(final Intent intent) {
+		List<Uri> uris = new ArrayList<>();
+		Uri uri = intent.getData();
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
+			ClipData clipData = intent.getClipData();
+			for(int i = 0; i < clipData.getItemCount(); ++i) {
+				uris.add(clipData.getItemAt(i).getUri());
+			}
+		} else {
+			uris.add(uri);
+		}
+		return uris;
+	}
+
 	@Override
 	protected void onActivityResult(int requestCode, int resultCode,
 			final Intent data) {
@@ -972,25 +996,34 @@ public class ConversationActivity extends XmppActivity
 				mConversationFragment.hideSnackbar();
 				mConversationFragment.updateMessages();
 			} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
-				mPendingImageUri = data.getData();
+				mPendingImageUris.clear();
+				mPendingImageUris.addAll(extractUriFromIntent(data));
 				if (xmppConnectionServiceBound) {
-					attachImageToConversation(getSelectedConversation(),mPendingImageUri);
-					mPendingImageUri = null;
+					for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
+						attachImageToConversation(getSelectedConversation(),i.next());
+					}
 				}
 			} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
-				mPendingFileUri = data.getData();
+				mPendingFileUris.clear();
+				mPendingFileUris.addAll(extractUriFromIntent(data));
 				if (xmppConnectionServiceBound) {
-					attachFileToConversation(getSelectedConversation(),mPendingFileUri);
-					mPendingFileUri = null;
+					for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
+						attachFileToConversation(getSelectedConversation(), i.next());
+					}
 				}
-			} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO && mPendingImageUri != null) {
-				if (xmppConnectionServiceBound) {
-					attachImageToConversation(getSelectedConversation(),mPendingImageUri);
-					mPendingImageUri = null;
+			} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
+				if (mPendingImageUris.size() == 1) {
+					Uri uri = mPendingImageUris.get(0);
+					if (xmppConnectionServiceBound) {
+						attachImageToConversation(getSelectedConversation(), uri);
+						mPendingImageUris.clear();
+					}
+					Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+					intent.setData(uri);
+					sendBroadcast(intent);
+				} else {
+					mPendingImageUris.clear();
 				}
-				Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
-				intent.setData(mPendingImageUri);
-				sendBroadcast(intent);
 			} else if (requestCode == ATTACHMENT_CHOICE_LOCATION) {
 				double latitude = data.getDoubleExtra("latitude",0);
 				double longitude = data.getDoubleExtra("longitude",0);
@@ -1000,10 +1033,6 @@ public class ConversationActivity extends XmppActivity
 					this.mPendingGeoUri = null;
 				}
 			}
-		} else {
-			if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
-				mPendingImageUri = null;
-			}
 		}
 	}
 

src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java 🔗

@@ -18,6 +18,7 @@ import java.net.URLConnection;
 import java.net.URLDecoder;
 import java.nio.charset.UnsupportedCharsetException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import eu.siacs.conversations.Config;
@@ -32,7 +33,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
 public class ShareWithActivity extends XmppActivity {
 
 	private class Share {
-		public Uri uri;
+		public List<Uri> uris = new ArrayList<>();
 		public boolean image;
 		public String account;
 		public String contact;
@@ -104,7 +105,7 @@ public class ShareWithActivity extends XmppActivity {
 					int position, long arg3) {
 				Conversation conversation = mConversations.get(position);
 				if (conversation.getMode() == Conversation.MODE_SINGLE
-						|| share.uri == null) {
+						|| share.uris.size() == 0) {
 					share(mConversations.get(position));
 				}
 			}
@@ -133,18 +134,32 @@ public class ShareWithActivity extends XmppActivity {
 
 	@Override
 	public void onStart() {
-		final String type = getIntent().getType();
-		final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
-		if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) {
-			this.share.uri = uri;
-			this.share.image = type.startsWith("image/") || isImage(uri);
-		} else {
-			this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+		super.onStart();
+		Intent intent = getIntent();
+		if (intent == null) {
+			return;
+		}
+		final String type = intent.getType();
+		if (Intent.ACTION_SEND.equals(intent.getAction())) {
+			final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
+			if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) {
+				this.share.uris.add(uri);
+				this.share.image = type.startsWith("image/") || isImage(uri);
+			} else {
+				this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+			}
+		} else if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
+			this.share.image = type != null && type.startsWith("image/");
+			if (!this.share.image) {
+				return;
+			}
+
+			this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
 		}
 		if (xmppConnectionServiceBound) {
-			xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uri == null);
+			xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.image);
 		}
-		super.onStart();
+
 	}
 
 	protected boolean isImage(Uri uri) {
@@ -164,7 +179,7 @@ public class ShareWithActivity extends XmppActivity {
 			return;
 		}
 		xmppConnectionService.populateWithOrderedConversations(mConversations,
-				this.share != null && this.share.uri == null);
+				this.share != null && this.share.uris.size() == 0);
 	}
 
 	private void share() {
@@ -188,7 +203,7 @@ public class ShareWithActivity extends XmppActivity {
 	}
 
 	private void share(final Conversation conversation) {
-		if (share.uri != null) {
+		if (share.uris.size() != 0) {
 			selectPresence(conversation, new OnPresenceSelected() {
 				@Override
 				public void onPresenceSelected() {
@@ -196,22 +211,23 @@ public class ShareWithActivity extends XmppActivity {
 						Toast.makeText(getApplicationContext(),
 								getText(R.string.preparing_image),
 								Toast.LENGTH_LONG).show();
-						ShareWithActivity.this.xmppConnectionService
-							.attachImageToConversation(conversation, share.uri,
-									attachFileCallback);
+						for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) {
+							ShareWithActivity.this.xmppConnectionService
+									.attachImageToConversation(conversation, i.next(),
+											attachFileCallback);
+						}
 					} else {
 						Toast.makeText(getApplicationContext(),
 								getText(R.string.preparing_file),
 								Toast.LENGTH_LONG).show();
 						ShareWithActivity.this.xmppConnectionService
-							.attachFileToConversation(conversation, share.uri,
-									attachFileCallback);
+								.attachFileToConversation(conversation, share.uris.get(0),
+										attachFileCallback);
 					}
 					switchToConversation(conversation, null, true);
 					finish();
 				}
 			});
-
 		} else {
 			switchToConversation(conversation, this.share.text, true);
 			finish();