refactored file download status. make image http download available for carbon copied (sent) messages as well

iNPUTmice created

Change summary

res/layout/message_sent.xml                                         |   8 
src/eu/siacs/conversations/crypto/PgpEngine.java                    |   6 
src/eu/siacs/conversations/entities/Conversation.java               |   7 
src/eu/siacs/conversations/entities/Downloadable.java               |  14 
src/eu/siacs/conversations/entities/DownloadableFile.java           |   4 
src/eu/siacs/conversations/entities/Message.java                    |  55 
src/eu/siacs/conversations/http/HttpConnection.java                 |  84 
src/eu/siacs/conversations/http/HttpConnectionManager.java          |  11 
src/eu/siacs/conversations/parser/MessageParser.java                |   5 
src/eu/siacs/conversations/persistance/DatabaseBackend.java         |   2 
src/eu/siacs/conversations/persistance/FileBackend.java             |  10 
src/eu/siacs/conversations/services/AbstractConnectionManager.java  |   5 
src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java        |   3 
src/eu/siacs/conversations/ui/ContactDetailsActivity.java           |   9 
src/eu/siacs/conversations/ui/ConversationActivity.java             |   3 
src/eu/siacs/conversations/ui/ConversationFragment.java             |   9 
src/eu/siacs/conversations/ui/ManageAccountActivity.java            |   6 
src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java      | 100 
src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java        |   4 
src/eu/siacs/conversations/ui/adapter/MessageAdapter.java           |  46 
src/eu/siacs/conversations/xmpp/XmppConnection.java                 |   2 
src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java        |  85 
src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java |   2 
src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java   |   3 
24 files changed, 273 insertions(+), 210 deletions(-)

Detailed changes

res/layout/message_sent.xml 🔗

@@ -45,6 +45,14 @@
                 android:textColor="@color/primarytext"
                 android:textIsSelectable="true"
                 android:textSize="?attr/TextSizeBody" />
+            
+             <Button
+                android:id="@+id/download_button"
+                style="?android:attr/buttonStyleSmall"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/download_image"
+                android:visibility="gone" />
 
             <LinearLayout
                 android:layout_width="wrap_content"

src/eu/siacs/conversations/crypto/PgpEngine.java 🔗

@@ -76,7 +76,8 @@ public class PgpEngine {
 					case OpenPgpApi.RESULT_CODE_ERROR:
 						OpenPgpError error = result
 								.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
-						Log.d(Config.LOGTAG,"openpgp error: "+error.getMessage());
+						Log.d(Config.LOGTAG,
+								"openpgp error: " + error.getMessage());
 						callback.error(R.string.openpgp_error, message);
 						return;
 					default:
@@ -110,7 +111,8 @@ public class PgpEngine {
 									+ ',' + imageWidth + ',' + imageHeight);
 							message.setEncryption(Message.ENCRYPTION_DECRYPTED);
 							PgpEngine.this.mXmppConnectionService
-									.updateMessage(message);;
+									.updateMessage(message);
+							;
 							callback.success(message);
 							return;
 						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:

src/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -68,7 +68,7 @@ public class Conversation extends AbstractEntity {
 
 	private transient MucOptions mucOptions = null;
 
-	//private transient String latestMarkableMessageId;
+	// private transient String latestMarkableMessageId;
 
 	private byte[] symmetricKey;
 
@@ -142,8 +142,9 @@ public class Conversation extends AbstractEntity {
 		if (this.messages == null) {
 			return null;
 		}
-		for(int i = this.messages.size() - 1; i >= 0; --i) {
-			if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED && this.messages.get(i).markable) {
+		for (int i = this.messages.size() - 1; i >= 0; --i) {
+			if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
+					&& this.messages.get(i).markable) {
 				if (this.messages.get(i).isRead()) {
 					return null;
 				} else {

src/eu/siacs/conversations/entities/Downloadable.java 🔗

@@ -1,9 +1,19 @@
 package eu.siacs.conversations.entities;
 
 public interface Downloadable {
-	
+
 	public final String[] VALID_EXTENSIONS = { "webp", "jpeg", "jpg", "png" };
 	public final String[] VALID_CRYPTO_EXTENSIONS = { "pgp", "gpg", "otr" };
-	
+
+	public static final int STATUS_UNKNOWN = 0x200;
+	public static final int STATUS_CHECKING = 0x201;
+	public static final int STATUS_FAILED = 0x202;
+	public static final int STATUS_OFFER = 0x203;
+	public static final int STATUS_DOWNLOADING = 0x204;
+
 	public void start();
+
+	public int getStatus();
+
+	public long getFileSize();
 }

src/eu/siacs/conversations/entities/DownloadableFile.java 🔗

@@ -29,7 +29,7 @@ public class DownloadableFile extends File {
 	private long expectedSize = 0;
 	private String sha1sum;
 	private Key aeskey;
-	
+
 	private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
 			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
 
@@ -81,7 +81,7 @@ public class DownloadableFile extends File {
 	public Key getKey() {
 		return this.aeskey;
 	}
-	
+
 	public InputStream createInputStream() {
 		if (this.getKey() == null) {
 			try {

src/eu/siacs/conversations/entities/Message.java 🔗

@@ -14,10 +14,6 @@ public class Message extends AbstractEntity {
 
 	public static final String TABLENAME = "messages";
 
-	public static final int STATUS_RECEIVED_CHECKING = -4;
-	public static final int STATUS_RECEPTION_FAILED = -3;
-	public static final int STATUS_RECEIVED_OFFER = -2;
-	public static final int STATUS_RECEIVING = -1;
 	public static final int STATUS_RECEIVED = 0;
 	public static final int STATUS_UNSEND = 1;
 	public static final int STATUS_SEND = 2;
@@ -136,8 +132,8 @@ public class Message extends AbstractEntity {
 			if (this.trueCounterpart == null) {
 				return null;
 			} else {
-				return this.conversation.getAccount().getRoster().getContactFromRoster(
-						this.trueCounterpart);
+				return this.conversation.getAccount().getRoster()
+						.getContactFromRoster(this.trueCounterpart);
 			}
 		}
 	}
@@ -147,12 +143,9 @@ public class Message extends AbstractEntity {
 	}
 
 	public String getReadableBody(Context context) {
-		if ((encryption == ENCRYPTION_PGP) && (type == TYPE_TEXT)) {
+		if (encryption == ENCRYPTION_PGP) {
 			return context.getText(R.string.encrypted_message_received)
 					.toString();
-		} else if ((encryption == ENCRYPTION_OTR) && (type == TYPE_IMAGE)) {
-			return context.getText(R.string.encrypted_image_received)
-					.toString();
 		} else if (encryption == ENCRYPTION_DECRYPTION_FAILED) {
 			return context.getText(R.string.decryption_failed).toString();
 		} else if (type == TYPE_IMAGE) {
@@ -322,6 +315,8 @@ public class Message extends AbstractEntity {
 			return false;
 		}
 		return (message.getType() == Message.TYPE_TEXT
+				&& this.getDownloadable() == null
+				&& message.getDownloadable() == null
 				&& message.getEncryption() != Message.ENCRYPTION_PGP
 				&& this.getType() == message.getType()
 				&& this.getEncryption() == message.getEncryption()
@@ -368,26 +363,34 @@ public class Message extends AbstractEntity {
 			return prev.mergable(this);
 		}
 	}
-	
+
 	public boolean bodyContainsDownloadable() {
 		Contact contact = this.getContact();
-		if (contact == null || !contact.trusted()) {
+		if (status <= STATUS_RECEIVED
+				&& (contact == null || !contact.trusted())) {
 			return false;
 		}
 		try {
 			URL url = new URL(this.getBody());
-			if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
+			if (!url.getProtocol().equalsIgnoreCase("http")
+					&& !url.getProtocol().equalsIgnoreCase("https")) {
 				return false;
 			}
-			if (url.getPath()==null) {
+			if (url.getPath() == null) {
 				return false;
 			}
 			String[] pathParts = url.getPath().split("/");
 			String filename = pathParts[pathParts.length - 1];
 			String[] extensionParts = filename.split("\\.");
-			if (extensionParts.length == 2 && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(extensionParts[extensionParts.length -1])) {
+			if (extensionParts.length == 2
+					&& Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
+							extensionParts[extensionParts.length - 1])) {
 				return true;
-			} else if (extensionParts.length == 3 && Arrays.asList(Downloadable.VALID_CRYPTO_EXTENSIONS).contains(extensionParts.length -1) && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(extensionParts[extensionParts.length -2])) {
+			} else if (extensionParts.length == 3
+					&& Arrays.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
+							.contains(extensionParts.length - 1)
+					&& Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
+							extensionParts[extensionParts.length - 2])) {
 				return true;
 			} else {
 				return false;
@@ -396,27 +399,23 @@ public class Message extends AbstractEntity {
 			return false;
 		}
 	}
-	
+
 	public ImageParams getImageParams() {
 		ImageParams params = new ImageParams();
-		if (body==null) {
+		if (this.downloadable != null) {
+			params.size = this.downloadable.getFileSize();
+		}
+		if (body == null) {
 			return params;
 		}
 		String parts[] = body.split(",");
-		if (parts.length==1) {
+		if (parts.length == 1) {
 			try {
 				params.size = Long.parseLong(parts[0]);
 			} catch (NumberFormatException e) {
 				params.origin = parts[0];
 			}
-		} else if (parts.length == 2) {
-			params.origin = parts[0];
-			try {
-				params.size = Long.parseLong(parts[1]);
-			} catch (NumberFormatException e) {
-				params.size = 0;
-			}
-		} else if (parts.length==3) {
+		} else if (parts.length == 3) {
 			try {
 				params.size = Long.parseLong(parts[0]);
 			} catch (NumberFormatException e) {
@@ -452,7 +451,7 @@ public class Message extends AbstractEntity {
 		}
 		return params;
 	}
-	
+
 	public class ImageParams {
 		public long size = 0;
 		public int width = 0;

src/eu/siacs/conversations/http/HttpConnection.java 🔗

@@ -24,7 +24,8 @@ public class HttpConnection implements Downloadable {
 	private URL mUrl;
 	private Message message;
 	private DownloadableFile file;
-	private long mPreviousFileSize = Long.MIN_VALUE;
+	private long mPreviousFileSize = 0;
+	private int mStatus = Downloadable.STATUS_UNKNOWN;
 
 	public HttpConnection(HttpConnectionManager manager) {
 		this.mHttpConnectionManager = manager;
@@ -33,6 +34,7 @@ public class HttpConnection implements Downloadable {
 
 	@Override
 	public void start() {
+		changeStatus(STATUS_DOWNLOADING);
 		new Thread(new FileDownloader()).start();
 	}
 
@@ -41,43 +43,46 @@ public class HttpConnection implements Downloadable {
 		this.message.setDownloadable(this);
 		try {
 			mUrl = new URL(message.getBody());
-			this.file = mXmppConnectionService.getFileBackend().getConversationsFile(message,false);
-			message.setType(Message.TYPE_IMAGE);
-			message.setStatus(Message.STATUS_RECEIVED_CHECKING);
-			mXmppConnectionService.updateConversationUi();
+			this.file = mXmppConnectionService.getFileBackend()
+					.getConversationsFile(message, false);
 			checkFileSize();
 		} catch (MalformedURLException e) {
 			this.cancel();
 		}
 	}
-	
+
 	public void init(Message message, URL url) {
 		this.message = message;
 		this.message.setDownloadable(this);
 		this.mUrl = url;
-		this.file = mXmppConnectionService.getFileBackend().getConversationsFile(message,false);
+		this.file = mXmppConnectionService.getFileBackend()
+				.getConversationsFile(message, false);
 		this.mPreviousFileSize = message.getImageParams().size;
-		message.setType(Message.TYPE_IMAGE);
-		message.setStatus(Message.STATUS_RECEIVED_CHECKING);
-		mXmppConnectionService.updateConversationUi();
 		checkFileSize();
 	}
-	
+
 	private void checkFileSize() {
+		changeStatus(STATUS_CHECKING);
 		new Thread(new FileSizeChecker()).start();
 	}
 
 	public void cancel() {
-		mXmppConnectionService.markMessage(message, Message.STATUS_RECEPTION_FAILED);
 		mHttpConnectionManager.finishConnection(this);
+		message.setDownloadable(null);
+		message.setBody(mUrl.toString());
+		mXmppConnectionService.updateMessage(message);
 	}
-	
+
 	private void finish() {
-		message.setStatus(Message.STATUS_RECEIVED);
-		mXmppConnectionService.updateMessage(message);
+		message.setDownloadable(null);
 		mHttpConnectionManager.finishConnection(this);
 	}
 
+	private void changeStatus(int status) {
+		this.mStatus = status;
+		mXmppConnectionService.updateConversationUi();
+	}
+
 	private class FileSizeChecker implements Runnable {
 
 		@Override
@@ -90,21 +95,21 @@ public class HttpConnection implements Downloadable {
 				return;
 			}
 			file.setExpectedSize(size);
-			message.setBody(mUrl.toString()+","+String.valueOf(size));
-			if (size <= mHttpConnectionManager.getAutoAcceptFileSize() || size == mPreviousFileSize) {
-				mXmppConnectionService.updateMessage(message);
+			message.setType(Message.TYPE_IMAGE);
+			if (size <= mHttpConnectionManager.getAutoAcceptFileSize()
+					|| size == mPreviousFileSize) {
 				start();
 			} else {
-				message.setStatus(Message.STATUS_RECEIVED_OFFER);
-				mXmppConnectionService.updateMessage(message);
+				changeStatus(STATUS_OFFER);
 			}
 		}
 
 		private long retrieveFileSize() throws IOException {
-			HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
+			HttpURLConnection connection = (HttpURLConnection) mUrl
+					.openConnection();
 			connection.setRequestMethod("HEAD");
 			if (connection instanceof HttpsURLConnection) {
-				
+
 			}
 			String contentLength = connection.getHeaderField("Content-Length");
 			if (contentLength == null) {
@@ -118,13 +123,12 @@ public class HttpConnection implements Downloadable {
 		}
 
 	}
-	
+
 	private class FileDownloader implements Runnable {
 
 		@Override
 		public void run() {
 			try {
-				mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVING);
 				download();
 				updateImageBounds();
 				finish();
@@ -132,13 +136,15 @@ public class HttpConnection implements Downloadable {
 				cancel();
 			}
 		}
-		
+
 		private void download() throws IOException {
-			HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
+			HttpURLConnection connection = (HttpURLConnection) mUrl
+					.openConnection();
 			if (connection instanceof HttpsURLConnection) {
-				
+
 			}
-			BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
+			BufferedInputStream is = new BufferedInputStream(
+					connection.getInputStream());
 			OutputStream os = file.createOutputStream();
 			int count = -1;
 			byte[] buffer = new byte[1024];
@@ -149,17 +155,31 @@ public class HttpConnection implements Downloadable {
 			os.close();
 			is.close();
 		}
-		
+
 		private void updateImageBounds() {
 			BitmapFactory.Options options = new BitmapFactory.Options();
 			options.inJustDecodeBounds = true;
 			BitmapFactory.decodeFile(file.getAbsolutePath(), options);
 			int imageHeight = options.outHeight;
 			int imageWidth = options.outWidth;
-			message.setBody(mUrl.toString()+","+file.getSize() + ','
+			message.setBody(mUrl.toString() + "," + file.getSize() + ','
 					+ imageWidth + ',' + imageHeight);
-			
+			mXmppConnectionService.updateMessage(message);
+		}
+
+	}
+
+	@Override
+	public int getStatus() {
+		return this.mStatus;
+	}
+
+	@Override
+	public long getFileSize() {
+		if (this.file != null) {
+			return this.file.getExpectedSize();
+		} else {
+			return 0;
 		}
-		
 	}
 }

src/eu/siacs/conversations/http/HttpConnectionManager.java 🔗

@@ -13,24 +13,23 @@ public class HttpConnectionManager extends AbstractConnectionManager {
 	public HttpConnectionManager(XmppConnectionService service) {
 		super(service);
 	}
-	
+
 	private List<HttpConnection> connections = new CopyOnWriteArrayList<HttpConnection>();
-	
-	
+
 	public HttpConnection createNewConnection(Message message) {
 		HttpConnection connection = new HttpConnection(this);
 		connection.init(message);
 		this.connections.add(connection);
 		return connection;
 	}
-	
+
 	public HttpConnection createNewConnection(Message message, URL url) {
 		HttpConnection connection = new HttpConnection(this);
-		connection.init(message,url);
+		connection.init(message, url);
 		this.connections.add(connection);
 		return connection;
 	}
-	
+
 	public void finishConnection(HttpConnection connection) {
 		this.connections.remove(connection);
 	}

src/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -478,8 +478,9 @@ public class MessageParser extends AbstractParser implements
 				mXmppConnectionService.databaseBackend.createMessage(message);
 			}
 		}
-		if (message.getStatus() == Message.STATUS_RECEIVED && message.bodyContainsDownloadable()) {
-			this.mXmppConnectionService.getHttpConnectionManager().createNewConnection(message);
+		if (message.bodyContainsDownloadable()) {
+			this.mXmppConnectionService.getHttpConnectionManager()
+					.createNewConnection(message);
 		}
 		notify = notify && !conversation.isMuted();
 		if (notify) {

src/eu/siacs/conversations/persistance/FileBackend.java 🔗

@@ -70,7 +70,8 @@ public class FileBackend {
 		return getJingleFileLegacy(message, true);
 	}
 
-	public DownloadableFile getJingleFileLegacy(Message message, boolean decrypted) {
+	public DownloadableFile getJingleFileLegacy(Message message,
+			boolean decrypted) {
 		Conversation conversation = message.getConversation();
 		String prefix = context.getFilesDir().getAbsolutePath();
 		String path = prefix + "/" + conversation.getAccount().getJid() + "/"
@@ -92,7 +93,8 @@ public class FileBackend {
 		return getConversationsFile(message, true);
 	}
 
-	public DownloadableFile getConversationsFile(Message message, boolean decrypted) {
+	public DownloadableFile getConversationsFile(Message message,
+			boolean decrypted) {
 		StringBuilder filename = new StringBuilder();
 		filename.append(Environment.getExternalStoragePublicDirectory(
 				Environment.DIRECTORY_PICTURES).getAbsolutePath());
@@ -144,8 +146,8 @@ public class FileBackend {
 		return this.copyImageToPrivateStorage(message, image, 0);
 	}
 
-	private DownloadableFile copyImageToPrivateStorage(Message message, Uri image,
-			int sampleSize) throws ImageCopyException {
+	private DownloadableFile copyImageToPrivateStorage(Message message,
+			Uri image, int sampleSize) throws ImageCopyException {
 		try {
 			InputStream is = context.getContentResolver()
 					.openInputStream(image);

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

@@ -1,17 +1,16 @@
 package eu.siacs.conversations.services;
 
-
 public class AbstractConnectionManager {
 	protected XmppConnectionService mXmppConnectionService;
 
 	public AbstractConnectionManager(XmppConnectionService service) {
 		this.mXmppConnectionService = service;
 	}
-	
+
 	public XmppConnectionService getXmppConnectionService() {
 		return this.mXmppConnectionService;
 	}
-	
+
 	public long getAutoAcceptFileSize() {
 		String config = this.mXmppConnectionService.getPreferences().getString(
 				"auto_accept_file_size", "524288");

src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java 🔗

@@ -199,7 +199,8 @@ public class ConferenceDetailsActivity extends XmppActivity {
 	}
 
 	private void populateView() {
-		mAccountJid.setText(getString(R.string.using_account,conversation.getAccount().getJid()));
+		mAccountJid.setText(getString(R.string.using_account, conversation
+				.getAccount().getJid()));
 		mYourPhoto.setImageBitmap(conversation.getAccount().getImage(this, 48));
 		setTitle(conversation.getName());
 		mFullJid.setText(conversation.getContactJid().split("/", 2)[0]);

src/eu/siacs/conversations/ui/ContactDetailsActivity.java 🔗

@@ -56,7 +56,8 @@ public class ContactDetailsActivity extends XmppActivity {
 
 		@Override
 		public void onClick(DialogInterface dialog, int which) {
-			ContactDetailsActivity.this.xmppConnectionService.deleteContactOnServer(contact);
+			ContactDetailsActivity.this.xmppConnectionService
+					.deleteContactOnServer(contact);
 			ContactDetailsActivity.this.finish();
 		}
 	};
@@ -78,7 +79,8 @@ public class ContactDetailsActivity extends XmppActivity {
 
 		@Override
 		public void onClick(View v) {
-			AlertDialog.Builder builder = new AlertDialog.Builder(ContactDetailsActivity.this);
+			AlertDialog.Builder builder = new AlertDialog.Builder(
+					ContactDetailsActivity.this);
 			builder.setTitle(getString(R.string.action_add_phone_book));
 			builder.setMessage(getString(R.string.add_phone_book_text,
 					contact.getJid()));
@@ -309,7 +311,8 @@ public class ContactDetailsActivity extends XmppActivity {
 		} else {
 			contactJidTv.setText(contact.getJid());
 		}
-		accountJidTv.setText(getString(R.string.using_account,contact.getAccount().getJid()));
+		accountJidTv.setText(getString(R.string.using_account, contact
+				.getAccount().getJid()));
 
 		UIHelper.prepareContactBadge(this, badge, contact,
 				getApplicationContext());

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

@@ -222,7 +222,8 @@ public class ConversationActivity extends XmppActivity implements
 			ab.setDisplayHomeAsUpEnabled(true);
 			ab.setHomeButtonEnabled(true);
 			if (getSelectedConversation().getMode() == Conversation.MODE_SINGLE
-					|| ConversationActivity.this.useSubjectToIdentifyConference()) {
+					|| ConversationActivity.this
+							.useSubjectToIdentifyConference()) {
 				ab.setTitle(getSelectedConversation().getName());
 			} else {
 				ab.setTitle(getSelectedConversation().getContactJid()

src/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -72,8 +72,7 @@ public class ConversationFragment extends Fragment {
 	private boolean messagesLoaded = false;
 
 	private IntentSender askForPassphraseIntent = null;
-	
-	
+
 	private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<Message>();
 	private boolean mDecryptJobRunning = false;
 
@@ -505,8 +504,8 @@ public class ConversationFragment extends Fragment {
 	private void decryptNext() {
 		Message next = this.mEncryptedMessages.peek();
 		PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
-			
-		if (next!=null && engine != null && !mDecryptJobRunning) {
+
+		if (next != null && engine != null && !mDecryptJobRunning) {
 			mDecryptJobRunning = true;
 			engine.decrypt(next, new UiCallback<Message>() {
 
@@ -535,7 +534,7 @@ public class ConversationFragment extends Fragment {
 			});
 		}
 	}
-	
+
 	private void messageSent() {
 		int size = this.messageList.size();
 		messagesView.setSelection(size - 1);

src/eu/siacs/conversations/ui/ManageAccountActivity.java 🔗

@@ -70,7 +70,8 @@ public class ManageAccountActivity extends XmppActivity {
 	public void onCreateContextMenu(ContextMenu menu, View v,
 			ContextMenuInfo menuInfo) {
 		super.onCreateContextMenu(menu, v, menuInfo);
-		ManageAccountActivity.this.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
+		ManageAccountActivity.this.getMenuInflater().inflate(
+				R.menu.manageaccounts_context, menu);
 		AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
 		this.selectedAccount = accountList.get(acmi.position);
 		if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
@@ -187,7 +188,8 @@ public class ManageAccountActivity extends XmppActivity {
 	}
 
 	private void deleteAccount(final Account account) {
-		AlertDialog.Builder builder = new AlertDialog.Builder(ManageAccountActivity.this);
+		AlertDialog.Builder builder = new AlertDialog.Builder(
+				ManageAccountActivity.this);
 		builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
 		builder.setIconAttribute(android.R.attr.alertDialogIcon);
 		builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));

src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java 🔗

@@ -5,6 +5,7 @@ import java.util.List;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Downloadable;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.ui.ConversationActivity;
 import eu.siacs.conversations.ui.XmppActivity;
@@ -37,11 +38,11 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 			view = (View) inflater.inflate(R.layout.conversation_list_row,
 					parent, false);
 		}
-		Conversation conv = getItem(position);
+		Conversation conversation = getItem(position);
 		if (this.activity instanceof ConversationActivity) {
 			ConversationActivity activity = (ConversationActivity) this.activity;
 			if (!activity.isConversationsOverviewHideable()) {
-				if (conv == activity.getSelectedConversation()) {
+				if (conversation == activity.getSelectedConversation()) {
 					view.setBackgroundColor(activity
 							.getSecondaryBackgroundColor());
 				} else {
@@ -53,65 +54,76 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 		}
 		TextView convName = (TextView) view
 				.findViewById(R.id.conversation_name);
-		if (conv.getMode() == Conversation.MODE_SINGLE
+		if (conversation.getMode() == Conversation.MODE_SINGLE
 				|| activity.useSubjectToIdentifyConference()) {
-			convName.setText(conv.getName());
+			convName.setText(conversation.getName());
 		} else {
-			convName.setText(conv.getContactJid().split("/")[0]);
+			convName.setText(conversation.getContactJid().split("/")[0]);
 		}
-		TextView convLastMsg = (TextView) view
+		TextView mLastMessage = (TextView) view
 				.findViewById(R.id.conversation_lastmsg);
+		TextView mTimestamp = (TextView) view
+				.findViewById(R.id.conversation_lastupdate);
 		ImageView imagePreview = (ImageView) view
 				.findViewById(R.id.conversation_lastimage);
 
-		Message latestMessage = conv.getLatestMessage();
+		Message message = conversation.getLatestMessage();
 
-		if (latestMessage.getType() == Message.TYPE_TEXT
-				|| latestMessage.getType() == Message.TYPE_PRIVATE) {
-			if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)
-					&& (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
-				String body = Config.PARSE_EMOTICONS ? UIHelper
-						.transformAsciiEmoticons(latestMessage.getBody())
-						: latestMessage.getBody();
-				convLastMsg.setText(body);
-			} else {
-				convLastMsg.setText(R.string.encrypted_message_received);
-			}
-			convLastMsg.setVisibility(View.VISIBLE);
-			imagePreview.setVisibility(View.GONE);
-		} else if (latestMessage.getType() == Message.TYPE_IMAGE) {
-			if (latestMessage.getStatus() >= Message.STATUS_RECEIVED) {
-				convLastMsg.setVisibility(View.GONE);
-				imagePreview.setVisibility(View.VISIBLE);
-				activity.loadBitmap(latestMessage, imagePreview);
-			} else {
-				convLastMsg.setVisibility(View.VISIBLE);
+		if (!conversation.isRead()) {
+			convName.setTypeface(null, Typeface.BOLD);
+		} else {
+			convName.setTypeface(null, Typeface.NORMAL);
+		}
+
+		if (message.getType() == Message.TYPE_IMAGE
+				|| message.getDownloadable() != null) {
+			Downloadable d = message.getDownloadable();
+			if (d != null) {
+				mLastMessage.setVisibility(View.VISIBLE);
 				imagePreview.setVisibility(View.GONE);
-				if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
-					convLastMsg.setText(R.string.image_offered_for_download);
-				} else if (latestMessage.getStatus() == Message.STATUS_RECEIVING) {
-					convLastMsg.setText(R.string.receiving_image);
+				if (conversation.isRead()) {
+					mLastMessage.setTypeface(null, Typeface.ITALIC);
+				} else {
+					mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
+				}
+				if (d.getStatus() == Downloadable.STATUS_CHECKING) {
+					mLastMessage.setText(R.string.checking_image);
+				} else if (d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
+					mLastMessage.setText(R.string.receiving_image);
+				} else if (d.getStatus() == Downloadable.STATUS_OFFER) {
+					mLastMessage.setText(R.string.image_offered_for_download);
 				} else {
-					convLastMsg.setText("");
+					mLastMessage.setText("");
 				}
+			} else {
+				mLastMessage.setVisibility(View.GONE);
+				imagePreview.setVisibility(View.VISIBLE);
+				activity.loadBitmap(message, imagePreview);
 			}
-		}
-
-		if (!conv.isRead()) {
-			convName.setTypeface(null, Typeface.BOLD);
-			convLastMsg.setTypeface(null, Typeface.BOLD);
 		} else {
-			convName.setTypeface(null, Typeface.NORMAL);
-			convLastMsg.setTypeface(null, Typeface.NORMAL);
+			if ((message.getEncryption() != Message.ENCRYPTION_PGP)
+					&& (message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
+				String body = Config.PARSE_EMOTICONS ? UIHelper
+						.transformAsciiEmoticons(message.getBody()) : message
+						.getBody();
+				mLastMessage.setText(body);
+			} else {
+				mLastMessage.setText(R.string.encrypted_message_received);
+			}
+			if (!conversation.isRead()) {
+				mLastMessage.setTypeface(null, Typeface.BOLD);
+			} else {
+				mLastMessage.setTypeface(null, Typeface.NORMAL);
+			}
+			mLastMessage.setVisibility(View.VISIBLE);
+			imagePreview.setVisibility(View.GONE);
 		}
-
-		((TextView) view.findViewById(R.id.conversation_lastupdate))
-				.setText(UIHelper.readableTimeDifference(getContext(), conv
-						.getLatestMessage().getTimeSent()));
+		mTimestamp.setText(UIHelper.readableTimeDifference(getContext(),
+				conversation.getLatestMessage().getTimeSent()));
 
 		ImageView profilePicture = (ImageView) view
 				.findViewById(R.id.conversation_image);
-		profilePicture.setImageBitmap(conv.getImage(activity, 56));
+		profilePicture.setImageBitmap(conversation.getImage(activity, 56));
 
 		return view;
 	}

src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java 🔗

@@ -47,11 +47,11 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
 		@Override
 		protected void publishResults(CharSequence constraint,
 				FilterResults results) {
-			ArrayList filteredList = (ArrayList)results.values;
+			ArrayList filteredList = (ArrayList) results.values;
 			if (results != null && results.count > 0) {
 				clear();
 				for (Object c : filteredList) {
-					add((String)c);
+					add((String) c);
 				}
 				notifyDataSetChanged();
 			}

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

@@ -138,10 +138,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 			info = getContext().getString(R.string.send_rejected);
 			error = true;
 			break;
-		case Message.STATUS_RECEPTION_FAILED:
-			info = getContext().getString(R.string.reception_failed);
-			error = true;
-			break;
 		default:
 			if (multiReceived) {
 				Contact contact = message.getContact();
@@ -230,19 +226,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		viewHolder.messageBody.setVisibility(View.VISIBLE);
 		if (message.getBody() != null) {
 			if (message.getType() != Message.TYPE_PRIVATE) {
-				if (message.getType() == Message.TYPE_IMAGE) {
-					String orign = message.getImageParams().origin;
-					if (orign!=null) {
-						viewHolder.messageBody.setText(orign);
-					} else {
-						viewHolder.messageBody.setText(message.getBody());
-					}
-				} else {
-					String body = Config.PARSE_EMOTICONS ? UIHelper
-							.transformAsciiEmoticons(message.getMergedBody())
-							: message.getMergedBody();
-					viewHolder.messageBody.setText(body);
-				}
+				String body = Config.PARSE_EMOTICONS ? UIHelper
+						.transformAsciiEmoticons(message.getMergedBody())
+						: message.getMergedBody();
+				viewHolder.messageBody.setText(body);
 			} else {
 				String privateMarker;
 				if (message.getStatus() <= Message.STATUS_RECEIVED) {
@@ -347,6 +334,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 				viewHolder.contact_picture = (ImageView) view
 						.findViewById(R.id.message_photo);
 				viewHolder.contact_picture.setImageBitmap(getSelfBitmap());
+				viewHolder.download_button = (Button) view
+						.findViewById(R.id.download_button);
 				viewHolder.indicator = (ImageView) view
 						.findViewById(R.id.security_indicator);
 				viewHolder.image = (ImageView) view
@@ -366,15 +355,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 						.findViewById(R.id.message_box);
 				viewHolder.contact_picture = (ImageView) view
 						.findViewById(R.id.message_photo);
-
 				viewHolder.download_button = (Button) view
 						.findViewById(R.id.download_button);
-
 				if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
-
 					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
 							item.getConversation().getContact(), getContext()));
-
 				}
 				viewHolder.indicator = (ImageView) view
 						.findViewById(R.id.security_indicator);
@@ -483,14 +468,15 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 			}
 		}
 
-		if (item.getType() == Message.TYPE_IMAGE) {
-			if (item.getStatus() == Message.STATUS_RECEIVING) {
+		if (item.getType() == Message.TYPE_IMAGE
+				|| item.getDownloadable() != null) {
+			Downloadable d = item.getDownloadable();
+			if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
 				displayInfoMessage(viewHolder, R.string.receiving_image);
-			} else if (item.getStatus() == Message.STATUS_RECEIVED_CHECKING) {
+			} else if (d != null
+					&& d.getStatus() == Downloadable.STATUS_CHECKING) {
 				displayInfoMessage(viewHolder, R.string.checking_image);
-			} else if (item.getStatus() == Message.STATUS_RECEPTION_FAILED) {
-				displayTextMessage(viewHolder, item);
-			} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
+			} else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) {
 				viewHolder.image.setVisibility(View.GONE);
 				viewHolder.messageBody.setVisibility(View.GONE);
 				viewHolder.download_button.setVisibility(View.VISIBLE);
@@ -499,11 +485,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 
 							@Override
 							public void onClick(View v) {
-								if (!startDonwloadable(item)) {
-									activity.xmppConnectionService.markMessage(
-											item,
-											Message.STATUS_RECEPTION_FAILED);
-								}
+								startDonwloadable(item);
 							}
 						});
 			} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)

src/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -910,7 +910,7 @@ public class XmppConnection implements Runnable {
 	}
 
 	public void disconnect(boolean force) {
-		Log.d(Config.LOGTAG, account.getJid()+": disconnecting");
+		Log.d(Config.LOGTAG, account.getJid() + ": disconnecting");
 		try {
 			if (force) {
 				socket.close();

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

@@ -34,17 +34,18 @@ public class JingleConnection implements Downloadable {
 	private JingleConnectionManager mJingleConnectionManager;
 	private XmppConnectionService mXmppConnectionService;
 
-	public static final int STATUS_INITIATED = 0;
-	public static final int STATUS_ACCEPTED = 1;
-	public static final int STATUS_TERMINATED = 2;
-	public static final int STATUS_CANCELED = 3;
-	public static final int STATUS_FINISHED = 4;
-	public static final int STATUS_TRANSMITTING = 5;
-	public static final int STATUS_FAILED = 99;
+	protected static final int JINGLE_STATUS_INITIATED = 0;
+	protected static final int JINGLE_STATUS_ACCEPTED = 1;
+	protected static final int JINGLE_STATUS_TERMINATED = 2;
+	protected static final int JINGLE_STATUS_CANCELED = 3;
+	protected static final int JINGLE_STATUS_FINISHED = 4;
+	protected static final int JINGLE_STATUS_TRANSMITTING = 5;
+	protected static final int JINGLE_STATUS_FAILED = 99;
 
 	private int ibbBlockSize = 4096;
 
-	private int status = -1;
+	private int mJingleStatus = -1;
+	private int mStatus = -1;
 	private Message message;
 	private String sessionId;
 	private Account account;
@@ -76,7 +77,7 @@ public class JingleConnection implements Downloadable {
 					mXmppConnectionService.markMessage(message,
 							Message.STATUS_SEND_FAILED);
 				}
-				status = STATUS_FAILED;
+				mJingleStatus = JINGLE_STATUS_FAILED;
 			}
 		}
 	};
@@ -254,13 +255,14 @@ public class JingleConnection implements Downloadable {
 	}
 
 	public void init(Account account, JinglePacket packet) {
-		this.status = STATUS_INITIATED;
+		this.mJingleStatus = JINGLE_STATUS_INITIATED;
 		Conversation conversation = this.mXmppConnectionService
 				.findOrCreateConversation(account,
 						packet.getFrom().split("/", 2)[0], false);
 		this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
+		this.message.setStatus(Message.STATUS_RECEIVED);
 		this.message.setType(Message.TYPE_IMAGE);
-		this.message.setStatus(Message.STATUS_RECEIVED_OFFER);
+		this.mStatus = Downloadable.STATUS_OFFER;
 		this.message.setDownloadable(this);
 		String[] fromParts = packet.getFrom().split("/", 2);
 		this.message.setPresence(fromParts[1]);
@@ -306,6 +308,7 @@ public class JingleConnection implements Downloadable {
 					long size = Long.parseLong(fileSize.getContent());
 					message.setBody(Long.toString(size));
 					conversation.getMessages().add(message);
+					mXmppConnectionService.updateConversationUi();
 					if (size <= this.mJingleConnectionManager
 							.getAutoAcceptFileSize()) {
 						Log.d(Config.LOGTAG, "auto accepting file from "
@@ -370,7 +373,7 @@ public class JingleConnection implements Downloadable {
 			content.socks5transport().setChildren(getCandidatesAsElements());
 			packet.setContent(content);
 			this.sendJinglePacket(packet);
-			this.status = STATUS_INITIATED;
+			this.mJingleStatus = JINGLE_STATUS_INITIATED;
 		}
 	}
 
@@ -383,8 +386,9 @@ public class JingleConnection implements Downloadable {
 	}
 
 	private void sendAccept() {
-		status = STATUS_ACCEPTED;
-		mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVING);
+		mJingleStatus = JINGLE_STATUS_ACCEPTED;
+		this.mStatus = Downloadable.STATUS_DOWNLOADING;
+		mXmppConnectionService.updateConversationUi();
 		this.mJingleConnectionManager.getPrimaryCandidate(this.account,
 				new OnPrimaryCandidateFound() {
 
@@ -458,7 +462,7 @@ public class JingleConnection implements Downloadable {
 		Content content = packet.getJingleContent();
 		mergeCandidates(JingleCandidate.parse(content.socks5transport()
 				.getChildren()));
-		this.status = STATUS_ACCEPTED;
+		this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
 		mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
 		this.connectNextCandidate();
 		return true;
@@ -493,7 +497,8 @@ public class JingleConnection implements Downloadable {
 			} else if (content.socks5transport().hasChild("candidate-error")) {
 				Log.d(Config.LOGTAG, "received candidate error");
 				this.receivedCandidate = true;
-				if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
+				if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+						&& (this.sentCandidate)) {
 					this.connect();
 				}
 				return true;
@@ -505,7 +510,8 @@ public class JingleConnection implements Downloadable {
 					JingleCandidate candidate = getCandidate(cid);
 					candidate.flagAsUsedByCounterpart();
 					this.receivedCandidate = true;
-					if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
+					if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+							&& (this.sentCandidate)) {
 						this.connect();
 					} else {
 						Log.d(Config.LOGTAG,
@@ -533,7 +539,7 @@ public class JingleConnection implements Downloadable {
 				this.sendFallbackToIbb();
 			}
 		} else {
-			this.status = STATUS_TRANSMITTING;
+			this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
 			if (connection.needsActivation()) {
 				if (connection.getCandidate().isOurs()) {
 					Log.d(Config.LOGTAG, "candidate "
@@ -620,9 +626,10 @@ public class JingleConnection implements Downloadable {
 		packet.setReason(reason);
 		this.sendJinglePacket(packet);
 		this.disconnect();
-		this.status = STATUS_FINISHED;
-		this.mXmppConnectionService.markMessage(this.message,
-				Message.STATUS_RECEIVED);
+		this.mJingleStatus = JINGLE_STATUS_FINISHED;
+		this.message.setStatus(Message.STATUS_RECEIVED);
+		this.message.setDownloadable(null);
+		this.mXmppConnectionService.updateMessage(message);
 		this.mJingleConnectionManager.finishConnection(this);
 	}
 
@@ -692,7 +699,7 @@ public class JingleConnection implements Downloadable {
 	}
 
 	private void receiveSuccess() {
-		this.status = STATUS_FINISHED;
+		this.mJingleStatus = JINGLE_STATUS_FINISHED;
 		this.mXmppConnectionService.markMessage(this.message,
 				Message.STATUS_SEND);
 		this.disconnect();
@@ -700,14 +707,14 @@ public class JingleConnection implements Downloadable {
 	}
 
 	public void cancel() {
-		this.status = STATUS_CANCELED;
+		this.mJingleStatus = JINGLE_STATUS_CANCELED;
 		this.disconnect();
 		if (this.message != null) {
 			if (this.responder.equals(account.getFullJid())) {
-				this.mXmppConnectionService.markMessage(this.message,
-						Message.STATUS_RECEPTION_FAILED);
+				this.mStatus = Downloadable.STATUS_FAILED;
+				this.mXmppConnectionService.updateConversationUi();
 			} else {
-				if (this.status == STATUS_INITIATED) {
+				if (this.mJingleStatus == JINGLE_STATUS_INITIATED) {
 					this.mXmppConnectionService.markMessage(this.message,
 							Message.STATUS_SEND_REJECTED);
 				} else {
@@ -790,7 +797,7 @@ public class JingleConnection implements Downloadable {
 				.setAttribute("cid", cid);
 		packet.setContent(content);
 		this.sentCandidate = true;
-		if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
+		if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
 			connect();
 		}
 		this.sendJinglePacket(packet);
@@ -803,7 +810,7 @@ public class JingleConnection implements Downloadable {
 		content.socks5transport().addChild("candidate-error");
 		packet.setContent(content);
 		this.sentCandidate = true;
-		if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
+		if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
 			connect();
 		}
 		this.sendJinglePacket(packet);
@@ -817,8 +824,8 @@ public class JingleConnection implements Downloadable {
 		return this.responder;
 	}
 
-	public int getStatus() {
-		return this.status;
+	public int getJingleStatus() {
+		return this.mJingleStatus;
 	}
 
 	private boolean equalCandidateExists(JingleCandidate candidate) {
@@ -869,7 +876,7 @@ public class JingleConnection implements Downloadable {
 	}
 
 	public void start() {
-		if (status == STATUS_INITIATED) {
+		if (mJingleStatus == JINGLE_STATUS_INITIATED) {
 			new Thread(new Runnable() {
 
 				@Override
@@ -878,7 +885,21 @@ public class JingleConnection implements Downloadable {
 				}
 			}).start();
 		} else {
-			Log.d(Config.LOGTAG, "status (" + status + ") was not ok");
+			Log.d(Config.LOGTAG, "status (" + mJingleStatus + ") was not ok");
+		}
+	}
+
+	@Override
+	public int getStatus() {
+		return this.mStatus;
+	}
+
+	@Override
+	public long getFileSize() {
+		if (this.file != null) {
+			return this.file.getExpectedSize();
+		} else {
+			return 0;
 		}
 	}
 }

src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java 🔗

@@ -154,7 +154,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 
 	public void cancelInTransmission() {
 		for (JingleConnection connection : this.connections) {
-			if (connection.getStatus() == JingleConnection.STATUS_TRANSMITTING) {
+			if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
 				connection.cancel();
 			}
 		}

src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java 🔗

@@ -100,7 +100,8 @@ public class JingleInbandTransport extends JingleTransport {
 	}
 
 	@Override
-	public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) {
+	public void send(DownloadableFile file,
+			OnFileTransmissionStatusChanged callback) {
 		this.onFileTransmissionStatusChanged = callback;
 		this.file = file;
 		try {