request storage permission when needed on Android 6.0

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java        |  2 
src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java | 11 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java            | 56 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java          | 14 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java       |  3 
src/main/res/values/strings.xml                                              |  1 
6 files changed, 71 insertions(+), 16 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java 🔗

@@ -182,7 +182,7 @@ public class HttpDownloadConnection implements Transferable {
 				return;
 			}
 			file.setExpectedSize(size);
-			if (size <= mHttpConnectionManager.getAutoAcceptFileSize()) {
+			if (mHttpConnectionManager.hasStoragePermission() && size <= mHttpConnectionManager.getAutoAcceptFileSize()) {
 				HttpDownloadConnection.this.acceptedAutomatically = true;
 				new Thread(new FileDownloader(interactive)).start();
 			} else {

src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java 🔗

@@ -1,6 +1,9 @@
 package eu.siacs.conversations.services;
 
+import android.Manifest;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.PowerManager;
 import android.util.Log;
 import android.util.Pair;
@@ -51,6 +54,14 @@ public class AbstractConnectionManager {
 		}
 	}
 
+	public boolean hasStoragePermission() {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+			return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+		} else {
+			return true;
+		}
+	}
+
 	public static Pair<InputStream,Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
 		FileInputStream is;
 		int size;

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

@@ -1,5 +1,6 @@
 package eu.siacs.conversations.ui;
 
+import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.ActionBar;
 import android.app.AlertDialog;
@@ -10,6 +11,7 @@ import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.IntentSender.SendIntentException;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -49,6 +51,7 @@ import eu.siacs.conversations.entities.Blockable;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.Transferable;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
 import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
@@ -77,6 +80,7 @@ public class ConversationActivity extends XmppActivity
 	public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
 	public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208;
 	public static final int REQUEST_TRUST_KEYS_MENU = 0x0209;
+	public static final int REQUEST_START_DOWNLOAD = 0x0210;
 	public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
 	public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
 	public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
@@ -93,6 +97,7 @@ public class ConversationActivity extends XmppActivity
 	final private List<Uri> mPendingFileUris = new ArrayList<>();
 	private Uri mPendingGeoUri = null;
 	private boolean forbidProcessingPendings = false;
+	private Message mPendingDownloadableMessage = null;
 
 	private boolean conversationWasSelectedByKeyboard = false;
 
@@ -497,6 +502,11 @@ public class ConversationActivity extends XmppActivity
 	}
 
 	public void attachFile(final int attachmentChoice) {
+		if (attachmentChoice != ATTACHMENT_CHOICE_LOCATION) {
+			if (!hasStoragePermission(attachmentChoice)) {
+				return;
+			}
+		}
 		switch (attachmentChoice) {
 			case ATTACHMENT_CHOICE_LOCATION:
 				getPreferences().edit().putString("recently_used_quick_action","location").apply();
@@ -575,7 +585,51 @@ public class ConversationActivity extends XmppActivity
 		}
 	}
 
+	public boolean hasStoragePermission(int attachmentChoice) {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+			if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+				requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, attachmentChoice);
+				return false;
+			} else {
+				return true;
+			}
+		} else {
+			return true;
+		}
+	}
+
 	@Override
+	public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+		if (grantResults.length > 0)
+			if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+				if (requestCode == REQUEST_START_DOWNLOAD) {
+					if (this.mPendingDownloadableMessage != null) {
+						startDownloadable(this.mPendingDownloadableMessage);
+					}
+				} else {
+					attachFile(requestCode);
+				}
+			} else {
+				Toast.makeText(this,R.string.no_storage_permission,Toast.LENGTH_SHORT).show();
+		}
+	}
+
+	public void startDownloadable(Message message) {
+		if (!hasStoragePermission(ConversationActivity.REQUEST_START_DOWNLOAD)) {
+			this.mPendingDownloadableMessage = message;
+			return;
+		}
+		Transferable transferable = message.getTransferable();
+		if (transferable != null) {
+			if (!transferable.start()) {
+				Toast.makeText(this, R.string.not_connected_try_again,Toast.LENGTH_SHORT).show();
+			}
+		} else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
+			xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
+		}
+	}
+
+		@Override
 	public boolean onOptionsItemSelected(final MenuItem item) {
 		if (item.getItemId() == android.R.id.home) {
 			showConversationsOverview();
@@ -1176,7 +1230,7 @@ public class ConversationActivity extends XmppActivity
 			if (downloadUuid != null) {
 				final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
 				if (message != null) {
-					mConversationFragment.messageListAdapter.startDownloadable(message);
+					startDownloadable(message);
 				}
 			}
 		}

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -352,7 +352,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 
 			@Override
 			public void onClick(View v) {
-				startDownloadable(message);
+				activity.startDownloadable(message);
 			}
 		});
 		viewHolder.download_button.setOnLongClickListener(openContextMenu);
@@ -602,18 +602,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		return view;
 	}
 
-	public void startDownloadable(Message message) {
-		Transferable transferable = message.getTransferable();
-		if (transferable != null) {
-			if (!transferable.start()) {
-				Toast.makeText(activity, R.string.not_connected_try_again,
-						Toast.LENGTH_SHORT).show();
-			}
-		} else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
-			activity.xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
-		}
-	}
-
 	public void openDownloadable(Message message) {
 		DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
 		if (!file.exists()) {

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java 🔗

@@ -361,7 +361,8 @@ public class JingleConnection implements Transferable {
 				message.setBody(Long.toString(size));
 				conversation.add(message);
 				mXmppConnectionService.updateConversationUi();
-				if (size < this.mJingleConnectionManager.getAutoAcceptFileSize()) {
+				if (mJingleConnectionManager.hasStoragePermission()
+						&& size < this.mJingleConnectionManager.getAutoAcceptFileSize()) {
 					Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom());
 					this.acceptedAutomatically = true;
 					this.sendAccept();

src/main/res/values/strings.xml 🔗

@@ -557,4 +557,5 @@
 	</plurals>
 	<string name="shared_file_with_x">Shared file with %s</string>
 	<string name="shared_image_with_x">Shared image with %s</string>
+	<string name="no_storage_permission">Conversations need access to external storage</string>
 </resources>