context menu for messages. allow to resend single messages

This commit is contained in:
iNPUTmice 2014-10-23 21:27:41 +02:00
parent de3739970b
commit d73a77643d
11 changed files with 189 additions and 39 deletions

View file

@ -15,7 +15,8 @@
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_toRightOf="@+id/message_photo" android:layout_toRightOf="@+id/message_photo"
android:background="@drawable/message_border" android:background="@drawable/message_border"
android:minHeight="48dp" > android:minHeight="48dp"
android:longClickable="true">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -43,7 +44,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="web" android:autoLink="web"
android:textColor="@color/primarytext" android:textColor="@color/primarytext"
android:textIsSelectable="true"
android:textSize="?attr/TextSizeBody" /> android:textSize="?attr/TextSizeBody" />
<Button <Button

View file

@ -15,7 +15,8 @@
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_toLeftOf="@+id/message_photo" android:layout_toLeftOf="@+id/message_photo"
android:background="@drawable/message_border" android:background="@drawable/message_border"
android:minHeight="48dp" > android:minHeight="48dp"
android:longClickable="true">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -43,7 +44,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="web" android:autoLink="web"
android:textColor="@color/primarytext" android:textColor="@color/primarytext"
android:textIsSelectable="true"
android:textSize="?attr/TextSizeBody" /> android:textSize="?attr/TextSizeBody" />
<Button <Button

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/copy_text"
android:title="@string/copy_text"/>
<item
android:id="@+id/share_image"
android:title="@string/share_image"/>
<item
android:id="@+id/copy_url"
android:title="@string/copy_original_url" />
<item
android:id="@+id/send_again"
android:title="@string/send_again"/>
</menu>

View file

@ -249,10 +249,10 @@
<string name="pref_force_encryption">Force end-to-end encryption</string> <string name="pref_force_encryption">Force end-to-end encryption</string>
<string name="pref_force_encryption_summary">Always send messages encrypted (except for conferences)</string> <string name="pref_force_encryption_summary">Always send messages encrypted (except for conferences)</string>
<string name="pref_dont_save_encrypted">Dont save encrypted messages</string> <string name="pref_dont_save_encrypted">Dont save encrypted messages</string>
<string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string>
<string name="pref_enable_legacy_ssl">Enable legacy SSL</string> <string name="pref_enable_legacy_ssl">Enable legacy SSL</string>
<string name="pref_enable_legacy_ssl_summary">Enables SSLv3 support for legacy servers. Warning: SSLv3 is considered insecure.</string> <string name="pref_enable_legacy_ssl_summary">Enables SSLv3 support for legacy servers. Warning: SSLv3 is considered insecure.</string>
<string name="pref_expert_options">Expert options</string> <string name="pref_expert_options">Expert options</string>
<string name="pref_expert_options_summary">Please be careful with these</string> <string name="pref_expert_options_summary">Please be careful with these</string>
<string name="pref_use_larger_font">Increase font size</string> <string name="pref_use_larger_font">Increase font size</string>
<string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string> <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string>
@ -272,5 +272,14 @@
<string name="image_file_deleted">The image file has been deleted</string> <string name="image_file_deleted">The image file has been deleted</string>
<string name="not_connected_try_again">You are not connected. Try again later</string> <string name="not_connected_try_again">You are not connected. Try again later</string>
<string name="check_image_filesize">Check image file size</string> <string name="check_image_filesize">Check image file size</string>
<string name="message_options">Message options</string>
<string name="copy_text">Copy text</string>
<string name="share_image">Share image</string>
<string name="copy_original_url">Copy original URL</string>
<string name="send_again">Send again</string>
<string name="image_url">Image URL</string>
<string name="message_text">Message text</string>
<string name="url_copied_to_clipboard">URL copied to clipboard</string>
<string name="message_copied_to_clipboard">Message copied to clipboard</string>
</resources> </resources>

View file

@ -431,6 +431,11 @@ public class Message extends AbstractEntity {
params.size = Long.parseLong(parts[0]); params.size = Long.parseLong(parts[0]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
params.origin = parts[0]; params.origin = parts[0];
try {
params.url = new URL(parts[0]);
} catch (MalformedURLException e1) {
params.url = null;
}
} }
} else if (parts.length == 3) { } else if (parts.length == 3) {
try { try {
@ -450,6 +455,11 @@ public class Message extends AbstractEntity {
} }
} else if (parts.length == 4) { } else if (parts.length == 4) {
params.origin = parts[0]; params.origin = parts[0];
try {
params.url = new URL(parts[0]);
} catch (MalformedURLException e1) {
params.url = null;
}
try { try {
params.size = Long.parseLong(parts[1]); params.size = Long.parseLong(parts[1]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -470,6 +480,7 @@ public class Message extends AbstractEntity {
} }
public class ImageParams { public class ImageParams {
public URL url;
public long size = 0; public long size = 0;
public int width = 0; public int width = 0;
public int height = 0; public int height = 0;

View file

@ -1924,4 +1924,21 @@ public class XmppConnectionService extends Service {
} }
} }
public void resendFailedMessages(Message message) {
List<Message> messages = new ArrayList<Message>();
Message current = message;
while(current.getStatus() == Message.STATUS_SEND_FAILED) {
messages.add(current);
if (current.mergable(current.next())) {
current = current.next();
} else {
break;
}
}
for(Message msg: messages) {
markMessage(msg, Message.STATUS_WAITING);
this.resendMessage(msg);
}
}
} }

View file

@ -191,6 +191,7 @@ public class ConversationActivity extends XmppActivity implements
xmppConnectionService.getNotificationService() xmppConnectionService.getNotificationService()
.setOpenConversation(null); .setOpenConversation(null);
} }
closeContextMenu();
} }
@Override @Override

View file

@ -11,6 +11,7 @@ import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.entities.Presences;
@ -33,9 +34,12 @@ import android.content.IntentSender.SendIntentException;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.Selection; import android.text.Selection;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity; import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -45,6 +49,8 @@ import android.widget.AbsListView.OnScrollListener;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import android.widget.AbsListView; import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView; import android.widget.ListView;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
@ -193,6 +199,7 @@ public class ConversationFragment extends Fragment {
}; };
private ConversationActivity activity; private ConversationActivity activity;
private Message selectedMessage;
private void sendMessage() { private void sendMessage() {
if (this.conversation == null) { if (this.conversation == null) {
@ -326,9 +333,100 @@ public class ConversationFragment extends Fragment {
}); });
messagesView.setAdapter(messageListAdapter); messagesView.setAdapter(messageListAdapter);
registerForContextMenu(messagesView);
return view; return view;
} }
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
this.selectedMessage = this.messageList.get(acmi.position);
populateContextMenu(menu);
}
private void populateContextMenu(ContextMenu menu) {
if (this.selectedMessage.getType() != Message.TYPE_STATUS) {
activity.getMenuInflater().inflate(R.menu.message_context, menu);
menu.setHeaderTitle(R.string.message_options);
MenuItem copyText = menu.findItem(R.id.copy_text);
MenuItem shareImage = menu.findItem(R.id.share_image);
MenuItem sendAgain = menu.findItem(R.id.send_again);
MenuItem copyUrl = menu.findItem(R.id.copy_url);
if (this.selectedMessage.getType() != Message.TYPE_TEXT
|| this.selectedMessage.getDownloadable() != null) {
copyText.setVisible(false);
}
if (this.selectedMessage.getType() != Message.TYPE_IMAGE
|| (this.selectedMessage.getDownloadable() != null && this.selectedMessage
.getDownloadable().getStatus() == Downloadable.STATUS_DELETED)) {
shareImage.setVisible(false);
}
if (this.selectedMessage.getStatus() != Message.STATUS_SEND_FAILED) {
sendAgain.setVisible(false);
}
if ((this.selectedMessage.getType() != Message.TYPE_IMAGE && this.selectedMessage
.getDownloadable() == null)
|| this.selectedMessage.getImageParams().url == null) {
copyUrl.setVisible(false);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_image:
shareImage(selectedMessage);
return true;
case R.id.copy_text:
copyText(selectedMessage);
return true;
case R.id.send_again:
resendMessage(selectedMessage);
return true;
case R.id.copy_url:
copyUrl(selectedMessage);
return true;
default:
return super.onContextItemSelected(item);
}
}
private void shareImage(Message message) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM,
activity.xmppConnectionService.getFileBackend()
.getJingleFileUri(message));
shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setType("image/webp");
activity.startActivity(Intent.createChooser(shareIntent,
getText(R.string.share_with)));
}
private void copyText(Message message) {
if (activity.copyTextToClipboard(message.getMergedBody(),
R.string.message_text)) {
Toast.makeText(activity, R.string.message_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
}
}
private void resendMessage(Message message) {
activity.xmppConnectionService.resendFailedMessages(message);
}
private void copyUrl(Message message) {
if (activity.copyTextToClipboard(
message.getImageParams().url.toString(), R.string.image_url)) {
Toast.makeText(activity, R.string.url_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
}
}
protected void privateMessageWith(String counterpart) { protected void privateMessageWith(String counterpart) {
this.mEditMessage.setText(""); this.mEditMessage.setText("");
this.conversation.setNextPresence(counterpart); this.conversation.setNextPresence(counterpart);
@ -437,7 +535,8 @@ public class ConversationFragment extends Fragment {
for (Message message : this.conversation.getMessages()) { for (Message message : this.conversation.getMessages()) {
if (message.getEncryption() == Message.ENCRYPTION_PGP if (message.getEncryption() == Message.ENCRYPTION_PGP
&& (message.getStatus() == Message.STATUS_RECEIVED || message && (message.getStatus() == Message.STATUS_RECEIVED || message
.getStatus() >= Message.STATUS_SEND) && message.getDownloadable() == null) { .getStatus() >= Message.STATUS_SEND)
&& message.getDownloadable() == null) {
if (!mEncryptedMessages.contains(message)) { if (!mEncryptedMessages.contains(message)) {
mEncryptedMessages.add(message); mEncryptedMessages.add(message);
} }

View file

@ -389,7 +389,7 @@ public class EditAccountActivity extends XmppActivity {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (OtrFingerprintToClipBoard(fingerprint)) { if (copyTextToClipboard(fingerprint,R.string.otr_fingerprint)) {
Toast.makeText( Toast.makeText(
EditAccountActivity.this, EditAccountActivity.this,
R.string.toast_message_otr_fingerprint, R.string.toast_message_otr_fingerprint,
@ -409,15 +409,4 @@ public class EditAccountActivity extends XmppActivity {
this.mStats.setVisibility(View.GONE); this.mStats.setVisibility(View.GONE);
} }
} }
private boolean OtrFingerprintToClipBoard(String fingerprint) {
ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String label = getResources().getString(R.string.otr_fingerprint);
if (mClipBoardManager != null) {
ClipData mClipData = ClipData.newPlainText(label, fingerprint);
mClipBoardManager.setPrimaryClip(mClipData);
return true;
}
return false;
}
} }

View file

@ -21,6 +21,8 @@ import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -532,6 +534,17 @@ public abstract class XmppActivity extends Activity {
return ((int) (dp * metrics.density)); return ((int) (dp * metrics.density));
} }
public boolean copyTextToClipboard(String text,int labelResId) {
ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String label = getResources().getString(labelResId);
if (mClipBoardManager != null) {
ClipData mClipData = ClipData.newPlainText(label, text);
mClipBoardManager.setPrimaryClip(mClipData);
return true;
}
return false;
}
public AvatarService avatarService() { public AvatarService avatarService() {
return xmppConnectionService.getAvatarService(); return xmppConnectionService.getAvatarService();
} }

View file

@ -43,6 +43,15 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private OnContactPictureClicked mOnContactPictureClickedListener; private OnContactPictureClicked mOnContactPictureClickedListener;
private OnContactPictureLongClicked mOnContactPictureLongClickedListener; private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
private OnLongClickListener openContextMenu = new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
v.showContextMenu();
return true;
}
};
public MessageAdapter(ConversationActivity activity, List<Message> messages) { public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages); super(activity, 0, messages);
this.activity = activity; this.activity = activity;
@ -259,6 +268,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
startDonwloadable(message); startDonwloadable(message);
} }
}); });
viewHolder.download_button.setOnLongClickListener(openContextMenu);
} }
private void displayImageMessage(ViewHolder viewHolder, private void displayImageMessage(ViewHolder viewHolder,
@ -292,23 +302,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
getContext().startActivity(intent); getContext().startActivity(intent);
} }
}); });
viewHolder.image.setOnLongClickListener(new OnLongClickListener() { viewHolder.image.setOnLongClickListener(openContextMenu);
@Override
public boolean onLongClick(View v) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM,
activity.xmppConnectionService.getFileBackend()
.getJingleFileUri(message));
shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setType("image/webp");
getContext().startActivity(
Intent.createChooser(shareIntent,
getContext().getText(R.string.share_with)));
return true;
}
});
} }
@Override @Override