From bbbc30e8234f14c9f340e0c29bb6b91a4bf640ee Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 27 Sep 2015 19:17:44 +0200 Subject: allow tab completion in conferences --- .../conversations/ui/ConversationFragment.java | 43 ++++++++++++++++++++++ .../eu/siacs/conversations/ui/EditMessage.java | 20 ++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index aced2798b..64101539b 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -37,6 +37,7 @@ import android.widget.Toast; import net.java.otr4j.session.SessionStatus; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentLinkedQueue; @@ -1226,6 +1227,48 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa updateSendButton(); } + private int completionIndex = 0; + private int lastCompletionLength = 0; + private String incomplete; + private int lastCompletionCursor; + private boolean firstWord = false; + + @Override + public boolean onTabPressed(boolean repeated) { + if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) { + return false; + } + if (repeated) { + completionIndex++; + } else { + lastCompletionLength = 0; + completionIndex = 0; + final String content = mEditMessage.getText().toString(); + lastCompletionCursor = mEditMessage.getSelectionEnd(); + int start = lastCompletionCursor > 0 ? content.lastIndexOf(" ",lastCompletionCursor-1) + 1 : 0; + firstWord = start == 0; + incomplete = content.substring(start,lastCompletionCursor); + } + List completions = new ArrayList<>(); + for(MucOptions.User user : conversation.getMucOptions().getUsers()) { + if (user.getName().startsWith(incomplete)) { + completions.add(user.getName()+(firstWord ? ": " : " ")); + } + } + Collections.sort(completions); + if (completions.size() > completionIndex) { + String completion = completions.get(completionIndex).substring(incomplete.length()); + mEditMessage.getEditableText().delete(lastCompletionCursor,lastCompletionCursor + lastCompletionLength); + mEditMessage.getEditableText().insert(lastCompletionCursor, completion); + lastCompletionLength = completion.length(); + } else { + completionIndex = -1; + mEditMessage.getEditableText().delete(lastCompletionCursor,lastCompletionCursor + lastCompletionLength); + lastCompletionLength = 0; + } + return true; + } + @Override public void onActivityResult(int requestCode, int resultCode, final Intent data) { diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java index 72975bb76..968ce6698 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java +++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java @@ -32,14 +32,24 @@ public class EditMessage extends EditText { private boolean isUserTyping = false; + private boolean lastInputWasTab = false; + protected KeyboardListener keyboardListener; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER && !event.isShiftPressed()) { + lastInputWasTab = false; if (keyboardListener != null && keyboardListener.onEnterPressed()) { return true; } + } else if (keyCode == KeyEvent.KEYCODE_TAB) { + if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) { + lastInputWasTab = true; + return true; + } + } else { + lastInputWasTab = false; } return super.onKeyDown(keyCode, event); } @@ -47,6 +57,7 @@ public class EditMessage extends EditText { @Override public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text,start,lengthBefore,lengthAfter); + lastInputWasTab = false; if (this.mTypingHandler != null && this.keyboardListener != null) { this.mTypingHandler.removeCallbacks(mTypingTimeout); this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000); @@ -69,10 +80,11 @@ public class EditMessage extends EditText { } public interface KeyboardListener { - public boolean onEnterPressed(); - public void onTypingStarted(); - public void onTypingStopped(); - public void onTextDeleted(); + boolean onEnterPressed(); + void onTypingStarted(); + void onTypingStopped(); + void onTextDeleted(); + boolean onTabPressed(boolean repeated); } } -- cgit v1.2.3 From f4d6b676e94d12377b5a2833ce8da4ad9dfc2d05 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 27 Sep 2015 21:27:07 +0200 Subject: catch rare activity not found exception when opening downloaded files --- .../eu/siacs/conversations/ui/adapter/MessageAdapter.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 2b48f19c9..7733edc7e 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.ui.adapter; +import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -322,7 +323,7 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.messageBody.setText(""); } viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true)); - viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground,true)); + viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true)); viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? R.color.grey800 : R.color.grey500)); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); viewHolder.messageBody.setTextIsSelectable(true); @@ -612,10 +613,14 @@ public class MessageAdapter extends ArrayAdapter { PackageManager manager = activity.getPackageManager(); List infos = manager.queryIntentActivities(openIntent, 0); if (infos.size() > 0) { - getContext().startActivity(openIntent); - } else { - Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show(); + try { + getContext().startActivity(openIntent); + return; + } catch (ActivityNotFoundException e) { + //ignored + } } + Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show(); } public void showLocation(Message message) { -- cgit v1.2.3 From 8881b71079e86e5beb55a2a84ca2c0fc427f497a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 28 Sep 2015 14:36:10 +0200 Subject: do tab completion only if neither ctrl nor alt are being pressed --- src/main/java/eu/siacs/conversations/ui/EditMessage.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java index 968ce6698..fc655b0ce 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java +++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java @@ -37,13 +37,13 @@ public class EditMessage extends EditText { protected KeyboardListener keyboardListener; @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_ENTER && !event.isShiftPressed()) { + public boolean onKeyDown(int keyCode, KeyEvent e) { + if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) { lastInputWasTab = false; if (keyboardListener != null && keyboardListener.onEnterPressed()) { return true; } - } else if (keyCode == KeyEvent.KEYCODE_TAB) { + } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) { if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) { lastInputWasTab = true; return true; @@ -51,7 +51,7 @@ public class EditMessage extends EditText { } else { lastInputWasTab = false; } - return super.onKeyDown(keyCode, event); + return super.onKeyDown(keyCode, e); } @Override -- cgit v1.2.3 From 5fb77a9739912c9a418b91cb2acfe54c3c88825d Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 28 Sep 2015 15:36:55 +0200 Subject: fixed NPE when executing rename callback in muc --- src/main/java/eu/siacs/conversations/entities/MucOptions.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index cf49cf65e..cf078ee52 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -308,7 +308,9 @@ public class MucOptions { this.error = ERROR_NO_ERROR; self = user; if (mNickChangingInProgress) { - onRenameListener.onSuccess(); + if (onRenameListener != null) { + onRenameListener.onSuccess(); + } mNickChangingInProgress = false; } else if (this.onJoinListener != null) { this.onJoinListener.onSuccess(); -- cgit v1.2.3 From 64dbb069abdb360a7b398c64daf022619b070693 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 29 Sep 2015 12:25:32 +0200 Subject: rotate thumbnails. fixes #1438 --- .../conversations/persistance/FileBackend.java | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 817f1c514..f5549f926 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -260,6 +260,10 @@ public class FileBackend { } } + private int getRotation(File file) { + return getRotation(Uri.parse("file://"+file.getAbsolutePath())); + } + private int getRotation(Uri image) { InputStream is = null; try { @@ -274,8 +278,7 @@ public class FileBackend { public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws FileNotFoundException { - Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get( - message.getUuid()); + Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get(message.getUuid()); if ((thumbnail == null) && (!cacheOnly)) { File file = getFile(message); BitmapFactory.Options options = new BitmapFactory.Options(); @@ -285,8 +288,12 @@ public class FileBackend { throw new FileNotFoundException(); } thumbnail = resize(fullsize, size); - this.mXmppConnectionService.getBitmapCache().put(message.getUuid(), - thumbnail); + fullsize.recycle(); + int rotation = getRotation(file); + if (rotation > 0) { + thumbnail = rotate(thumbnail, rotation); + } + this.mXmppConnectionService.getBitmapCache().put(message.getUuid(),thumbnail); } return thumbnail; } @@ -512,8 +519,10 @@ public class FileBackend { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), options); - int imageHeight = options.outHeight; - int imageWidth = options.outWidth; + int rotation = getRotation(file); + boolean rotated = rotation == 90 || rotation == 270; + int imageHeight = rotated ? options.outWidth : options.outHeight; + int imageWidth = rotated ? options.outHeight : options.outWidth; if (url == null) { message.setBody(Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight); } else { -- cgit v1.2.3 From 648e29db2cd8e5b170a90e93c5957b77ce1b0e8e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 29 Sep 2015 19:24:52 +0200 Subject: only invoke MTM in interactive mode after direct user input fixes #1027 fixes #792 fixes #1439 --- .../conversations/services/XmppConnectionService.java | 19 +++++++++++-------- .../eu/siacs/conversations/xmpp/XmppConnection.java | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 11 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ee0ccd4f7..fe5272b00 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -282,7 +282,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) { databaseBackend.updateAccount(account); - reconnectAccount(account, true); + reconnectAccount(account, true, false); } else if ((account.getStatus() != Account.State.CONNECTING) && (account.getStatus() != Account.State.NO_INTERNET)) { if (connection != null) { @@ -442,6 +442,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public int onStartCommand(Intent intent, int flags, int startId) { final String action = intent == null ? null : intent.getAction(); + boolean interactive = false; if (action != null) { switch (action) { case ConnectivityManager.CONNECTIVITY_ACTION: @@ -468,6 +469,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa break; case ACTION_TRY_AGAIN: resetAllAttemptCounts(false); + interactive = true; break; case ACTION_DISABLE_ACCOUNT: try { @@ -508,7 +510,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (lastSent > lastReceived) { if (pingTimeoutIn < 0) { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout"); - this.reconnectAccount(account, true); + this.reconnectAccount(account, true, interactive); } else { int secs = (int) (pingTimeoutIn / 1000); this.scheduleWakeUpCall(secs,account.getUuid().hashCode()); @@ -521,18 +523,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode()); } } else if (account.getStatus() == Account.State.OFFLINE) { - reconnectAccount(account,true); + reconnectAccount(account,true, interactive); } else if (account.getStatus() == Account.State.CONNECTING) { long timeout = Config.CONNECT_TIMEOUT - ((SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000); if (timeout < 0) { Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting"); - reconnectAccount(account, true); + reconnectAccount(account, true, interactive); } else { scheduleWakeUpCall((int) timeout,account.getUuid().hashCode()); } } else { if (account.getXmppConnection().getTimeToNextAttempt() <= 0) { - reconnectAccount(account, true); + reconnectAccount(account, true, interactive); } } @@ -1208,7 +1210,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void updateAccount(final Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); - reconnectAccount(account, false); + reconnectAccount(account, false, true); updateAccountUi(); getNotificationService().updateErrorNotification(); } @@ -2145,7 +2147,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.databaseBackend.updateConversation(conversation); } - public void reconnectAccount(final Account account, final boolean force) { + private void reconnectAccount(final Account account, final boolean force, final boolean interactive) { synchronized (account) { if (account.getXmppConnection() != null) { disconnect(account, force); @@ -2165,6 +2167,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa account.setXmppConnection(createConnection(account)); } Thread thread = new Thread(account.getXmppConnection()); + account.getXmppConnection().setInteractive(interactive); thread.start(); scheduleWakeUpCall(Config.CONNECT_TIMEOUT, account.getUuid().hashCode()); } else { @@ -2178,7 +2181,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa new Thread(new Runnable() { @Override public void run() { - reconnectAccount(account,false); + reconnectAccount(account,false,true); } }).start(); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index fd6465021..9527ee23d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -42,6 +42,7 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import de.duenndns.ssl.MemorizingTrustManager; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.sasl.DigestMd5; import eu.siacs.conversations.crypto.sasl.Plain; @@ -100,6 +101,7 @@ public class XmppConnection implements Runnable { private long lastPingSent = 0; private long lastConnect = 0; private long lastSessionStarted = 0; + private boolean mInteractive = false; private int attempt = 0; private final Hashtable> packetCallbacks = new Hashtable<>(); private OnPresencePacketReceived presenceListener = null; @@ -515,9 +517,15 @@ public class XmppConnection implements Runnable { tagReader.readTag(); try { final SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null,new X509TrustManager[]{this.mXmppConnectionService.getMemorizingTrustManager()},mXmppConnectionService.getRNG()); + MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager(); + sc.init(null,new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()},mXmppConnectionService.getRNG()); final SSLSocketFactory factory = sc.getSocketFactory(); - final HostnameVerifier verifier = this.mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier()); + final HostnameVerifier verifier; + if (mInteractive) { + verifier = trustManager.wrapHostnameVerifier(new StrictHostnameVerifier()); + } else { + verifier = trustManager.wrapHostnameVerifierNonInteractive(new StrictHostnameVerifier()); + } final InetAddress address = socket == null ? null : socket.getInetAddress(); if (factory == null || address == null || verifier == null) { @@ -839,7 +847,7 @@ public class XmppConnection implements Runnable { sendEnableCarbons(); } if (getFeatures().blocking() && !features.blockListRequested) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": Requesting block list"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list"); this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser()); } } @@ -1138,6 +1146,10 @@ public class XmppConnection implements Runnable { this.lastConnect = 0; } + public void setInteractive(boolean interactive) { + this.mInteractive = interactive; + } + private class Info { public final ArrayList features = new ArrayList<>(); public final ArrayList> identities = new ArrayList<>(); -- cgit v1.2.3 From 9dcf074a79856b866d38174c248ef16987e7ea16 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 30 Sep 2015 23:42:02 +0200 Subject: request stanza count after every ibb data stanza to not fill our own stanza queue --- .../java/eu/siacs/conversations/xmpp/XmppConnection.java | 14 ++++++++------ .../conversations/xmpp/jingle/JingleInbandTransport.java | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 9527ee23d..c41b69748 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -957,7 +957,6 @@ public class XmppConnection implements Runnable { disconnect(true); return; } - final String name = packet.getName(); tagWriter.writeStanzaAsync(packet); if (packet instanceof AbstractAcknowledgeableStanza) { AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet; @@ -973,9 +972,7 @@ public class XmppConnection implements Runnable { } public void sendPing() { - if (streamFeatures.hasChild("sm")) { - tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); - } else { + if (!r()) { final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setFrom(account.getJid()); iq.addChild("ping", "urn:xmpp:ping"); @@ -1086,8 +1083,13 @@ public class XmppConnection implements Runnable { return null; } - public void r() { - this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); + public boolean r() { + if (getFeatures().sm()) { + this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); + return true; + } else { + return false; + } } public String getMucServer() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index 85280c5ce..0b0cb4083 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -176,6 +176,7 @@ public class JingleInbandTransport extends JingleTransport { data.setAttribute("sid", this.sessionId); data.setContent(base64); this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived); + this.account.getXmppConnection().r(); //don't fill up stanza queue too much this.seq++; if (this.remainingSize > 0) { connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); -- cgit v1.2.3