Merge branch 'feature-gboardgifs' of https://github.com/illegalprime/Conversations into illegalprime-feature-gboardgifs
This commit is contained in:
commit
470d244414
|
@ -26,7 +26,7 @@ dependencies {
|
||||||
playstoreCompile 'com.google.android.gms:play-services-gcm:9.4.0'
|
playstoreCompile 'com.google.android.gms:play-services-gcm:9.4.0'
|
||||||
compile 'org.sufficientlysecure:openpgp-api:10.0'
|
compile 'org.sufficientlysecure:openpgp-api:10.0'
|
||||||
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
|
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
|
||||||
compile 'com.android.support:support-v13:24.2.0'
|
compile 'com.android.support:support-v13:25.1.0'
|
||||||
compile 'org.bouncycastle:bcprov-jdk15on:1.52'
|
compile 'org.bouncycastle:bcprov-jdk15on:1.52'
|
||||||
compile 'org.bouncycastle:bcmail-jdk15on:1.52'
|
compile 'org.bouncycastle:bcmail-jdk15on:1.52'
|
||||||
compile 'org.jitsi:org.otr4j:0.22'
|
compile 'org.jitsi:org.otr4j:0.22'
|
||||||
|
|
|
@ -55,6 +55,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.utils.ExifHelper;
|
import eu.siacs.conversations.utils.ExifHelper;
|
||||||
import eu.siacs.conversations.utils.FileUtils;
|
import eu.siacs.conversations.utils.FileUtils;
|
||||||
import eu.siacs.conversations.utils.FileWriterException;
|
import eu.siacs.conversations.utils.FileWriterException;
|
||||||
|
import eu.siacs.conversations.utils.MimeUtils;
|
||||||
import eu.siacs.conversations.xmpp.pep.Avatar;
|
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||||
|
|
||||||
public class FileBackend {
|
public class FileBackend {
|
||||||
|
@ -276,7 +277,7 @@ public class FileBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
|
public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
|
||||||
String mime = mXmppConnectionService.getContentResolver().getType(uri);
|
String mime = MimeUtils.guessMimeTypeFromUri(mXmppConnectionService, uri);
|
||||||
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime="+mime+")");
|
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime="+mime+")");
|
||||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
|
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
|
||||||
if (extension == null) {
|
if (extension == null) {
|
||||||
|
|
|
@ -98,6 +98,7 @@ import eu.siacs.conversations.ui.UiCallback;
|
||||||
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.utils.ExceptionHelper;
|
import eu.siacs.conversations.utils.ExceptionHelper;
|
||||||
|
import eu.siacs.conversations.utils.MimeUtils;
|
||||||
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
|
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
|
||||||
import eu.siacs.conversations.utils.PRNGFixes;
|
import eu.siacs.conversations.utils.PRNGFixes;
|
||||||
import eu.siacs.conversations.utils.PhoneHelper;
|
import eu.siacs.conversations.utils.PhoneHelper;
|
||||||
|
@ -493,9 +494,13 @@ public class XmppConnectionService extends Service {
|
||||||
callback.error(R.string.security_error_invalid_file_access, null);
|
callback.error(R.string.security_error_invalid_file_access, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String mimeType = MimeUtils.guessMimeTypeFromUri(this, uri);
|
||||||
final String compressPictures = getCompressPicturesPreference();
|
final String compressPictures = getCompressPicturesPreference();
|
||||||
|
|
||||||
if ("never".equals(compressPictures)
|
if ("never".equals(compressPictures)
|
||||||
|| ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))) {
|
|| ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))
|
||||||
|
|| (mimeType != null && mimeType.endsWith("/gif"))) {
|
||||||
Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+ ": not compressing picture. sending as file");
|
Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+ ": not compressing picture. sending as file");
|
||||||
attachFileToConversation(conversation, uri, callback);
|
attachFileToConversation(conversation, uri, callback);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1560,6 +1560,10 @@ public class ConversationActivity extends XmppActivity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void attachImageToConversation(Uri uri) {
|
||||||
|
this.attachImageToConversation(getSelectedConversation(), uri);
|
||||||
|
}
|
||||||
|
|
||||||
private void attachImageToConversation(Conversation conversation, Uri uri) {
|
private void attachImageToConversation(Conversation conversation, Uri uri) {
|
||||||
if (conversation == null) {
|
if (conversation == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -11,6 +11,8 @@ import android.content.Intent;
|
||||||
import android.content.IntentSender.SendIntentException;
|
import android.content.IntentSender.SendIntentException;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||||
|
import android.support.v13.view.inputmethod.InputContentInfoCompat;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -285,6 +287,37 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private EditMessage.OnCommitContentListener mEditorContentListener = new EditMessage.OnCommitContentListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] contentMimeTypes) {
|
||||||
|
// try to get permission to read the image, if applicable
|
||||||
|
if ((flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
|
||||||
|
try {
|
||||||
|
inputContentInfo.requestPermission();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(Config.LOGTAG, "InputContentInfoCompat#requestPermission() failed.", e);
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
activity.getString(R.string.no_permission_to_access_x, inputContentInfo.getDescription()),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the image
|
||||||
|
activity.attachImageToConversation(inputContentInfo.getContentUri());
|
||||||
|
|
||||||
|
// TODO: revoke permissions?
|
||||||
|
// since uploading an image is async its tough to wire a callback to when
|
||||||
|
// the image has finished uploading.
|
||||||
|
// According to the docs: "calling IC#releasePermission() is just to be a
|
||||||
|
// good citizen. Even if we failed to call that method, the system would eventually revoke
|
||||||
|
// the permission sometime after inputContentInfo object gets garbage-collected."
|
||||||
|
// See: https://developer.android.com/samples/CommitContentSampleApp/src/com.example.android.commitcontent.app/MainActivity.html#l164
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
private OnClickListener mSendButtonListener = new OnClickListener() {
|
private OnClickListener mSendButtonListener = new OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -416,6 +449,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||||
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
|
final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
|
||||||
view.setOnClickListener(null);
|
view.setOnClickListener(null);
|
||||||
|
|
||||||
|
String[] allImagesMimeType = {"image/*"};
|
||||||
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
|
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
|
||||||
mEditMessage.setOnClickListener(new OnClickListener() {
|
mEditMessage.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@ -427,6 +462,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mEditMessage.setOnEditorActionListener(mEditorActionListener);
|
mEditMessage.setOnEditorActionListener(mEditorActionListener);
|
||||||
|
mEditMessage.setRichContentListener(allImagesMimeType, mEditorContentListener);
|
||||||
|
|
||||||
mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
|
mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
|
||||||
mSendButton.setOnClickListener(this.mSendButtonListener);
|
mSendButton.setOnClickListener(this.mSendButtonListener);
|
||||||
|
|
|
@ -1,19 +1,33 @@
|
||||||
package eu.siacs.conversations.ui;
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.support.v13.view.inputmethod.EditorInfoCompat;
|
||||||
|
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||||
|
import android.support.v13.view.inputmethod.InputContentInfoCompat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputFilter;
|
import android.text.InputFilter;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
|
||||||
public class EditMessage extends EditText {
|
public class EditMessage extends EditText {
|
||||||
|
|
||||||
|
public interface OnCommitContentListener {
|
||||||
|
boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnCommitContentListener mCommitContentListener = null;
|
||||||
|
private String[] mimeTypes = null;
|
||||||
|
|
||||||
public EditMessage(Context context, AttributeSet attrs) {
|
public EditMessage(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
}
|
}
|
||||||
|
@ -125,4 +139,27 @@ public class EditMessage extends EditText {
|
||||||
return super.onTextContextMenuItem(id);
|
return super.onTextContextMenuItem(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) {
|
||||||
|
this.mimeTypes = mimeTypes;
|
||||||
|
this.mCommitContentListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
|
||||||
|
final InputConnection ic = super.onCreateInputConnection(editorInfo);
|
||||||
|
|
||||||
|
if (mimeTypes != null && mCommitContentListener != null) {
|
||||||
|
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
|
||||||
|
return InputConnectionCompat.createWrapper(ic, editorInfo, new InputConnectionCompat.OnCommitContentListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
|
||||||
|
return EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ic;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package eu.siacs.conversations.utils;
|
package eu.siacs.conversations.utils;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -484,4 +487,22 @@ public final class MimeUtils {
|
||||||
}
|
}
|
||||||
return mimeTypeToExtensionMap.get(mimeType);
|
return mimeTypeToExtensionMap.get(mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String guessMimeTypeFromUri(Context context, Uri uri) {
|
||||||
|
// try the content resolver
|
||||||
|
String mimeType = context.getContentResolver().getType(uri);
|
||||||
|
// try the extension
|
||||||
|
if (mimeType == null && uri.getPath() != null) {
|
||||||
|
String path = uri.getPath();
|
||||||
|
int start = path.lastIndexOf('.') + 1;
|
||||||
|
if (start < path.length()) {
|
||||||
|
mimeType = MimeUtils.guessMimeTypeFromExtension(path.substring(start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sometimes this works (as with the commit content api)
|
||||||
|
if (mimeType == null) {
|
||||||
|
mimeType = uri.getQueryParameter("mimeType");
|
||||||
|
}
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue