added search result context menu + date separators
This commit is contained in:
parent
59b5dece96
commit
6712a2d91f
|
@ -33,11 +33,16 @@ import android.database.Cursor;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
public class IndividualMessage extends Message {
|
public class IndividualMessage extends Message {
|
||||||
|
|
||||||
|
|
||||||
|
private IndividualMessage(Conversational conversation) {
|
||||||
|
super(conversation);
|
||||||
|
}
|
||||||
|
|
||||||
private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, String edited, boolean oob, String errorMessage, Set<ReadByMarker> readByMarkers, boolean markable) {
|
private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, String edited, boolean oob, String errorMessage, Set<ReadByMarker> readByMarkers, boolean markable) {
|
||||||
super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable);
|
super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable);
|
||||||
}
|
}
|
||||||
|
@ -57,6 +62,14 @@ public class IndividualMessage extends Message {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Message createDateSeparator(Message message) {
|
||||||
|
final Message separator = new IndividualMessage(message.getConversation());
|
||||||
|
separator.setType(Message.TYPE_STATUS);
|
||||||
|
separator.body = MessageAdapter.DATE_SEPARATOR_BODY;
|
||||||
|
separator.setTime(message.getTimeSent());
|
||||||
|
return separator;
|
||||||
|
}
|
||||||
|
|
||||||
public static Message fromCursor(Cursor cursor, Conversational conversation) {
|
public static Message fromCursor(Cursor cursor, Conversational conversation) {
|
||||||
Jid jid;
|
Jid jid;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -105,7 +105,7 @@ public class Message extends AbstractEntity {
|
||||||
private List<MucOptions.User> counterparts;
|
private List<MucOptions.User> counterparts;
|
||||||
private WeakReference<MucOptions.User> user;
|
private WeakReference<MucOptions.User> user;
|
||||||
|
|
||||||
private Message(Conversational conversation) {
|
protected Message(Conversational conversation) {
|
||||||
this.conversation = conversation;
|
this.conversation = conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,14 +229,6 @@ public class Message extends AbstractEntity {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Message createDateSeparator(Message message) {
|
|
||||||
final Message separator = new Message(message.getConversation());
|
|
||||||
separator.setType(Message.TYPE_STATUS);
|
|
||||||
separator.body = MessageAdapter.DATE_SEPARATOR_BODY;
|
|
||||||
separator.setTime(message.getTimeSent());
|
|
||||||
return separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
|
@ -85,6 +85,7 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
||||||
import eu.siacs.conversations.ui.util.ActivityResult;
|
import eu.siacs.conversations.ui.util.ActivityResult;
|
||||||
import eu.siacs.conversations.ui.util.AttachmentTool;
|
import eu.siacs.conversations.ui.util.AttachmentTool;
|
||||||
import eu.siacs.conversations.ui.util.ConversationMenuConfigurator;
|
import eu.siacs.conversations.ui.util.ConversationMenuConfigurator;
|
||||||
|
import eu.siacs.conversations.ui.util.DateSeparator;
|
||||||
import eu.siacs.conversations.ui.util.ListViewUtils;
|
import eu.siacs.conversations.ui.util.ListViewUtils;
|
||||||
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
||||||
import eu.siacs.conversations.ui.util.PendingItem;
|
import eu.siacs.conversations.ui.util.PendingItem;
|
||||||
|
@ -92,6 +93,7 @@ import eu.siacs.conversations.ui.util.PresenceSelector;
|
||||||
import eu.siacs.conversations.ui.util.ScrollState;
|
import eu.siacs.conversations.ui.util.ScrollState;
|
||||||
import eu.siacs.conversations.ui.util.SendButtonAction;
|
import eu.siacs.conversations.ui.util.SendButtonAction;
|
||||||
import eu.siacs.conversations.ui.util.SendButtonTool;
|
import eu.siacs.conversations.ui.util.SendButtonTool;
|
||||||
|
import eu.siacs.conversations.ui.util.ShareUtil;
|
||||||
import eu.siacs.conversations.ui.widget.EditMessage;
|
import eu.siacs.conversations.ui.widget.EditMessage;
|
||||||
import eu.siacs.conversations.utils.MessageUtils;
|
import eu.siacs.conversations.utils.MessageUtils;
|
||||||
import eu.siacs.conversations.utils.NickValidityChecker;
|
import eu.siacs.conversations.utils.NickValidityChecker;
|
||||||
|
@ -1111,13 +1113,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
public boolean onContextItemSelected(MenuItem item) {
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.share_with:
|
case R.id.share_with:
|
||||||
shareWith(selectedMessage);
|
ShareUtil.share(activity, selectedMessage);
|
||||||
return true;
|
return true;
|
||||||
case R.id.correct_message:
|
case R.id.correct_message:
|
||||||
correctMessage(selectedMessage);
|
correctMessage(selectedMessage);
|
||||||
return true;
|
return true;
|
||||||
case R.id.copy_message:
|
case R.id.copy_message:
|
||||||
copyMessage(selectedMessage);
|
ShareUtil.copyToClipboard(activity, selectedMessage);
|
||||||
return true;
|
return true;
|
||||||
case R.id.quote_message:
|
case R.id.quote_message:
|
||||||
quoteMessage(selectedMessage);
|
quoteMessage(selectedMessage);
|
||||||
|
@ -1126,7 +1128,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
resendMessage(selectedMessage);
|
resendMessage(selectedMessage);
|
||||||
return true;
|
return true;
|
||||||
case R.id.copy_url:
|
case R.id.copy_url:
|
||||||
copyUrl(selectedMessage);
|
ShareUtil.copyUrlToClipboard(activity, selectedMessage);
|
||||||
return true;
|
return true;
|
||||||
case R.id.download_file:
|
case R.id.download_file:
|
||||||
startDownloadable(selectedMessage);
|
startDownloadable(selectedMessage);
|
||||||
|
@ -1570,43 +1572,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shareWith(Message message) {
|
|
||||||
Intent shareIntent = new Intent();
|
|
||||||
shareIntent.setAction(Intent.ACTION_SEND);
|
|
||||||
if (message.isGeoUri()) {
|
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
|
|
||||||
shareIntent.setType("text/plain");
|
|
||||||
} else if (!message.isFileOrImage()) {
|
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getMergedBody().toString());
|
|
||||||
shareIntent.setType("text/plain");
|
|
||||||
} else {
|
|
||||||
final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
|
||||||
try {
|
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, FileBackend.getUriForFile(getActivity(), file));
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
Toast.makeText(getActivity(), activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
String mime = message.getMimeType();
|
|
||||||
if (mime == null) {
|
|
||||||
mime = "*/*";
|
|
||||||
}
|
|
||||||
shareIntent.setType(mime);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
//This should happen only on faulty androids because normally chooser is always available
|
|
||||||
Toast.makeText(getActivity(), R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyMessage(Message message) {
|
|
||||||
if (activity.copyTextToClipboard(message.getMergedBody().toString(), R.string.message)) {
|
|
||||||
Toast.makeText(getActivity(), R.string.message_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteFile(Message message) {
|
private void deleteFile(Message message) {
|
||||||
if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
|
if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
|
||||||
|
@ -1653,24 +1618,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyUrl(Message message) {
|
|
||||||
final String url;
|
|
||||||
final int resId;
|
|
||||||
if (message.isGeoUri()) {
|
|
||||||
resId = R.string.location;
|
|
||||||
url = message.getBody();
|
|
||||||
} else if (message.hasFileOnRemoteHost()) {
|
|
||||||
resId = R.string.file_url;
|
|
||||||
url = message.getFileParams().url.toString();
|
|
||||||
} else {
|
|
||||||
url = message.getBody().trim();
|
|
||||||
resId = R.string.file_url;
|
|
||||||
}
|
|
||||||
if (activity.copyTextToClipboard(url, resId)) {
|
|
||||||
Toast.makeText(getActivity(), R.string.url_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelTransmission(Message message) {
|
private void cancelTransmission(Message message) {
|
||||||
Transferable transferable = message.getTransferable();
|
Transferable transferable = message.getTransferable();
|
||||||
if (transferable != null) {
|
if (transferable != null) {
|
||||||
|
@ -1943,6 +1890,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
final String downloadUuid = extras.getString(ConversationsActivity.EXTRA_DOWNLOAD_UUID);
|
final String downloadUuid = extras.getString(ConversationsActivity.EXTRA_DOWNLOAD_UUID);
|
||||||
final String text = extras.getString(ConversationsActivity.EXTRA_TEXT);
|
final String text = extras.getString(ConversationsActivity.EXTRA_TEXT);
|
||||||
final String nick = extras.getString(ConversationsActivity.EXTRA_NICK);
|
final String nick = extras.getString(ConversationsActivity.EXTRA_NICK);
|
||||||
|
final boolean asQuote = extras.getBoolean(ConversationsActivity.EXTRA_AS_QUOTE);
|
||||||
final boolean pm = extras.getBoolean(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, false);
|
final boolean pm = extras.getBoolean(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, false);
|
||||||
if (nick != null) {
|
if (nick != null) {
|
||||||
if (pm) {
|
if (pm) {
|
||||||
|
@ -1959,9 +1907,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
highlightInConference(nick);
|
highlightInConference(nick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (text != null && asQuote) {
|
||||||
|
quoteText(text);
|
||||||
} else {
|
} else {
|
||||||
appendText(text);
|
appendText(text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final Message message = downloadUuid == null ? null : conversation.findMessageWithFileAndUuid(downloadUuid);
|
final Message message = downloadUuid == null ? null : conversation.findMessageWithFileAndUuid(downloadUuid);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
startDownloadable(message);
|
startDownloadable(message);
|
||||||
|
@ -2159,13 +2111,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
|
|
||||||
protected void updateDateSeparators() {
|
protected void updateDateSeparators() {
|
||||||
synchronized (this.messageList) {
|
synchronized (this.messageList) {
|
||||||
for (int i = 0; i < this.messageList.size(); ++i) {
|
DateSeparator.addAll(this.messageList);
|
||||||
final Message current = this.messageList.get(i);
|
|
||||||
if (i == 0 || !UIHelper.sameDay(this.messageList.get(i - 1).getTimeSent(), current.getTimeSent())) {
|
|
||||||
this.messageList.add(i, Message.createDateSeparator(current));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
|
||||||
public static final String EXTRA_CONVERSATION = "conversationUuid";
|
public static final String EXTRA_CONVERSATION = "conversationUuid";
|
||||||
public static final String EXTRA_DOWNLOAD_UUID = "eu.siacs.conversations.download_uuid";
|
public static final String EXTRA_DOWNLOAD_UUID = "eu.siacs.conversations.download_uuid";
|
||||||
public static final String EXTRA_TEXT = "text";
|
public static final String EXTRA_TEXT = "text";
|
||||||
|
public static final String EXTRA_AS_QUOTE = "as_quote";
|
||||||
public static final String EXTRA_NICK = "nick";
|
public static final String EXTRA_NICK = "nick";
|
||||||
public static final String EXTRA_IS_PRIVATE_MESSAGE = "pm";
|
public static final String EXTRA_IS_PRIVATE_MESSAGE = "pm";
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ThemeHelper.fixTextSize(snackbar);
|
ThemeHelper.fix(snackbar);
|
||||||
snackbar.show();
|
snackbar.show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,10 +36,14 @@ import android.text.Editable;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.ContextMenu;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -47,13 +51,20 @@ import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.databinding.ActivitySearchBinding;
|
import eu.siacs.conversations.databinding.ActivitySearchBinding;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.entities.StubConversation;
|
||||||
import eu.siacs.conversations.services.MessageSearchTask;
|
import eu.siacs.conversations.services.MessageSearchTask;
|
||||||
import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
||||||
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
|
import eu.siacs.conversations.ui.util.ChangeWatcher;
|
||||||
import eu.siacs.conversations.ui.util.Color;
|
import eu.siacs.conversations.ui.util.Color;
|
||||||
|
import eu.siacs.conversations.ui.util.DateSeparator;
|
||||||
import eu.siacs.conversations.ui.util.Drawable;
|
import eu.siacs.conversations.ui.util.Drawable;
|
||||||
import eu.siacs.conversations.ui.util.ListViewUtils;
|
import eu.siacs.conversations.ui.util.ListViewUtils;
|
||||||
|
import eu.siacs.conversations.ui.util.ShareUtil;
|
||||||
|
import eu.siacs.conversations.utils.MessageUtils;
|
||||||
|
|
||||||
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
|
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
|
||||||
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.showKeyboard;
|
import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.showKeyboard;
|
||||||
|
@ -63,6 +74,8 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
|
||||||
private ActivitySearchBinding binding;
|
private ActivitySearchBinding binding;
|
||||||
private MessageAdapter messageListAdapter;
|
private MessageAdapter messageListAdapter;
|
||||||
private final List<Message> messages = new ArrayList<>();
|
private final List<Message> messages = new ArrayList<>();
|
||||||
|
private WeakReference<Message> selectedMessageReference = new WeakReference<>(null);
|
||||||
|
private final ChangeWatcher<String> currentSearch = new ChangeWatcher<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(final Bundle savedInstanceState) {
|
public void onCreate(final Bundle savedInstanceState) {
|
||||||
|
@ -73,6 +86,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
|
||||||
this.messageListAdapter = new MessageAdapter(this, this.messages);
|
this.messageListAdapter = new MessageAdapter(this, this.messages);
|
||||||
this.messageListAdapter.setOnContactPictureClicked(this);
|
this.messageListAdapter.setOnContactPictureClicked(this);
|
||||||
this.binding.searchResults.setAdapter(messageListAdapter);
|
this.binding.searchResults.setAdapter(messageListAdapter);
|
||||||
|
registerForContextMenu(this.binding.searchResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,6 +101,24 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
|
AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||||
|
final Message message = this.messages.get(acmi.position);
|
||||||
|
this.selectedMessageReference = new WeakReference<>(message);
|
||||||
|
getMenuInflater().inflate(R.menu.search_result_context, menu);
|
||||||
|
MenuItem copy = menu.findItem(R.id.copy_message);
|
||||||
|
MenuItem quote = menu.findItem(R.id.quote_message);
|
||||||
|
MenuItem copyUrl = menu.findItem(R.id.copy_url);
|
||||||
|
if (message.isGeoUri()) {
|
||||||
|
copy.setVisible(false);
|
||||||
|
quote.setVisible(false);
|
||||||
|
} else {
|
||||||
|
copyUrl.setVisible(false);
|
||||||
|
}
|
||||||
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (item.getItemId() == android.R.id.home) {
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
@ -95,6 +127,43 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
final Message message = selectedMessageReference.get();
|
||||||
|
if (message != null) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.share_with:
|
||||||
|
ShareUtil.share(this, message);
|
||||||
|
break;
|
||||||
|
case R.id.copy_message:
|
||||||
|
ShareUtil.copyToClipboard(this, message);
|
||||||
|
break;
|
||||||
|
case R.id.copy_url:
|
||||||
|
ShareUtil.copyUrlToClipboard(this, message);
|
||||||
|
break;
|
||||||
|
case R.id.quote_message:
|
||||||
|
quote(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onContextItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void quote(Message message) {
|
||||||
|
String text = MessageUtils.prepareQuote(message);
|
||||||
|
final Conversational conversational = message.getConversation();
|
||||||
|
final Conversation conversation;
|
||||||
|
if (conversational instanceof Conversation) {
|
||||||
|
conversation = (Conversation) conversational;
|
||||||
|
} else {
|
||||||
|
conversation = xmppConnectionService.findOrCreateConversation(conversational.getAccount(),
|
||||||
|
conversational.getJid(),
|
||||||
|
conversational.getMode() == Conversational.MODE_MULTI,
|
||||||
|
true,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
switchToConversationAndQuote(conversation, text);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void refreshUiReal() {
|
protected void refreshUiReal() {
|
||||||
|
|
||||||
|
@ -129,7 +198,10 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable s) {
|
||||||
String term = s.toString().trim();
|
final String term = s.toString().trim();
|
||||||
|
if (!currentSearch.watch(term)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (term.length() > 0) {
|
if (term.length() > 0) {
|
||||||
xmppConnectionService.search(s.toString().trim(), this);
|
xmppConnectionService.search(s.toString().trim(), this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,6 +216,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
|
||||||
public void onSearchResultsAvailable(String term, List<Message> messages) {
|
public void onSearchResultsAvailable(String term, List<Message> messages) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
this.messages.clear();
|
this.messages.clear();
|
||||||
|
DateSeparator.addAll(messages);
|
||||||
this.messages.addAll(messages);
|
this.messages.addAll(messages);
|
||||||
messageListAdapter.notifyDataSetChanged();
|
messageListAdapter.notifyDataSetChanged();
|
||||||
changeBackground(true, messages.size() > 0);
|
changeBackground(true, messages.size() > 0);
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
|
||||||
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ThemeHelper.fixTextSize(this.snackBar);
|
ThemeHelper.fix(this.snackBar);
|
||||||
|
|
||||||
this.binding.shareButton.setOnClickListener(view -> {
|
this.binding.shareButton.setOnClickListener(view -> {
|
||||||
final Intent result = new Intent();
|
final Intent result = new Intent();
|
||||||
|
|
|
@ -467,25 +467,31 @@ public abstract class XmppActivity extends ActionBarActivity {
|
||||||
switchToConversation(conversation, null, false);
|
switchToConversation(conversation, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToConversation(Conversation conversation, String text,
|
public void switchToConversationAndQuote(Conversation conversation, String text) {
|
||||||
boolean newTask) {
|
switchToConversation(conversation, text, true, null, false, false);
|
||||||
switchToConversation(conversation, text, null, false, newTask);
|
}
|
||||||
|
|
||||||
|
public void switchToConversation(Conversation conversation, String text, boolean newTask) {
|
||||||
|
switchToConversation(conversation, text, false, null, false, newTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void highlightInMuc(Conversation conversation, String nick) {
|
public void highlightInMuc(Conversation conversation, String nick) {
|
||||||
switchToConversation(conversation, null, nick, false, false);
|
switchToConversation(conversation, null, false, nick, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void privateMsgInMuc(Conversation conversation, String nick) {
|
public void privateMsgInMuc(Conversation conversation, String nick) {
|
||||||
switchToConversation(conversation, null, nick, true, false);
|
switchToConversation(conversation, null, false, nick, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchToConversation(Conversation conversation, String text, String nick, boolean pm, boolean newTask) {
|
private void switchToConversation(Conversation conversation, String text, boolean asQuote, String nick, boolean pm, boolean newTask) {
|
||||||
Intent intent = new Intent(this, ConversationsActivity.class);
|
Intent intent = new Intent(this, ConversationsActivity.class);
|
||||||
intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
|
intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
|
||||||
intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
|
intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
intent.putExtra(ConversationsActivity.EXTRA_TEXT, text);
|
intent.putExtra(ConversationsActivity.EXTRA_TEXT, text);
|
||||||
|
if (asQuote) {
|
||||||
|
intent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, asQuote);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (nick != null) {
|
if (nick != null) {
|
||||||
intent.putExtra(ConversationsActivity.EXTRA_NICK, nick);
|
intent.putExtra(ConversationsActivity.EXTRA_NICK, nick);
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.ui.util;
|
||||||
|
|
||||||
|
public class ChangeWatcher<T> {
|
||||||
|
|
||||||
|
private T object = null;
|
||||||
|
|
||||||
|
public synchronized boolean watch(T object) {
|
||||||
|
if (this.object == null) {
|
||||||
|
this.object = object;
|
||||||
|
return object != null;
|
||||||
|
} else {
|
||||||
|
final boolean changed = !this.object.equals(object);
|
||||||
|
this.object = object;
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
101
src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java
Normal file
101
src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.ui.util;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
|
import eu.siacs.conversations.ui.XmppActivity;
|
||||||
|
|
||||||
|
public class ShareUtil {
|
||||||
|
|
||||||
|
public static void share(XmppActivity activity, Message message) {
|
||||||
|
Intent shareIntent = new Intent();
|
||||||
|
shareIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
if (message.isGeoUri()) {
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
|
||||||
|
shareIntent.setType("text/plain");
|
||||||
|
} else if (!message.isFileOrImage()) {
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getMergedBody().toString());
|
||||||
|
shareIntent.setType("text/plain");
|
||||||
|
} else {
|
||||||
|
final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
||||||
|
try {
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_STREAM, FileBackend.getUriForFile(activity, file));
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Toast.makeText(activity, activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
String mime = message.getMimeType();
|
||||||
|
if (mime == null) {
|
||||||
|
mime = "*/*";
|
||||||
|
}
|
||||||
|
shareIntent.setType(mime);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
activity.startActivity(Intent.createChooser(shareIntent, activity.getText(R.string.share_with)));
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
//This should happen only on faulty androids because normally chooser is always available
|
||||||
|
Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyToClipboard(XmppActivity activity, Message message) {
|
||||||
|
if (activity.copyTextToClipboard(message.getMergedBody().toString(), R.string.message)) {
|
||||||
|
Toast.makeText(activity, R.string.message_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyUrlToClipboard(XmppActivity activity, Message message) {
|
||||||
|
final String url;
|
||||||
|
final int resId;
|
||||||
|
if (message.isGeoUri()) {
|
||||||
|
resId = R.string.location;
|
||||||
|
url = message.getBody();
|
||||||
|
} else if (message.hasFileOnRemoteHost()) {
|
||||||
|
resId = R.string.file_url;
|
||||||
|
url = message.getFileParams().url.toString();
|
||||||
|
} else {
|
||||||
|
url = message.getBody().trim();
|
||||||
|
resId = R.string.file_url;
|
||||||
|
}
|
||||||
|
if (activity.copyTextToClipboard(url, resId)) {
|
||||||
|
Toast.makeText(activity, R.string.url_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ import android.content.res.TypedArray;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.StyleRes;
|
import android.support.annotation.StyleRes;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -87,8 +88,9 @@ public class ThemeHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void fixTextSize(Snackbar snackbar) {
|
public static void fix(Snackbar snackbar) {
|
||||||
TypedArray typedArray = snackbar.getContext().obtainStyledAttributes(new int[]{R.attr.TextSizeBody1});
|
final Context context = snackbar.getContext();
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(new int[]{R.attr.TextSizeBody1});
|
||||||
final float size = typedArray.getDimension(0,0f);
|
final float size = typedArray.getDimension(0,0f);
|
||||||
typedArray.recycle();
|
typedArray.recycle();
|
||||||
if (size != 0f) {
|
if (size != 0f) {
|
||||||
|
@ -97,6 +99,7 @@ public class ThemeHelper {
|
||||||
if (text != null && action != null) {
|
if (text != null && action != null) {
|
||||||
text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
|
text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
|
||||||
action.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
|
action.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
|
||||||
|
action.setTextColor(ContextCompat.getColor(context, R.color.deep_purple_a100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
src/main/res/menu/search_result_context.xml
Normal file
45
src/main/res/menu/search_result_context.xml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
~
|
||||||
|
~ Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
~ are permitted provided that the following conditions are met:
|
||||||
|
~
|
||||||
|
~ 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
~ list of conditions and the following disclaimer.
|
||||||
|
~
|
||||||
|
~ 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
~ this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
~ other materials provided with the distribution.
|
||||||
|
~
|
||||||
|
~ 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
~ may be used to endorse or promote products derived from this software without
|
||||||
|
~ specific prior written permission.
|
||||||
|
~
|
||||||
|
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
~ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
~ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
~ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
~ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
~ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
~ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/share_with"
|
||||||
|
android:title="@string/share_with"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/copy_message"
|
||||||
|
android:title="@string/copy_to_clipboard"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/quote_message"
|
||||||
|
android:title="@string/quote"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/copy_url"
|
||||||
|
android:title="@string/copy_original_url"/>
|
||||||
|
</menu>
|
|
@ -22,6 +22,8 @@
|
||||||
<color name="orange500">#ffff9800</color>
|
<color name="orange500">#ffff9800</color>
|
||||||
<color name="deep_purple_a100">#ffB388FF</color>
|
<color name="deep_purple_a100">#ffB388FF</color>
|
||||||
<color name="deep_purple_a200">#ff7C4DFF</color>
|
<color name="deep_purple_a200">#ff7C4DFF</color>
|
||||||
|
<color name="deep_purple_a400">#ff651FFF</color>
|
||||||
|
<color name="deep_purple_a700">#ff6200EA</color>
|
||||||
<color name="green500">#ff4CAF50</color>
|
<color name="green500">#ff4CAF50</color>
|
||||||
<color name="green600">#ff43A047</color>
|
<color name="green600">#ff43A047</color>
|
||||||
<color name="green700">#ff388E3C</color>
|
<color name="green700">#ff388E3C</color>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<style name="ConversationsTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="ConversationsTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<item name="colorPrimary">@color/green600</item>
|
<item name="colorPrimary">@color/green600</item>
|
||||||
<item name="colorPrimaryDark">@color/green700</item>
|
<item name="colorPrimaryDark">@color/green800</item>
|
||||||
<item name="colorAccent">@color/deep_purple_a200</item>
|
<item name="colorAccent">@color/deep_purple_a200</item>
|
||||||
<item name="popupOverlayStyle">@style/ThemeOverlay.AppCompat.Light</item>
|
<item name="popupOverlayStyle">@style/ThemeOverlay.AppCompat.Light</item>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue