More ways to add images in compose
This commit is contained in:
parent
d14fc624f9
commit
c6ea406a61
|
@ -5,13 +5,10 @@ import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.ColorStateList;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.InsetDrawable;
|
|
||||||
import android.graphics.drawable.LayerDrawable;
|
import android.graphics.drawable.LayerDrawable;
|
||||||
import android.icu.text.BreakIterator;
|
import android.icu.text.BreakIterator;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -22,7 +19,6 @@ import android.provider.OpenableColumns;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputFilter;
|
import android.text.InputFilter;
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
import android.text.SpannableStringBuilder;
|
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
@ -78,17 +74,15 @@ import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
||||||
import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan;
|
import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan;
|
||||||
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.text.SpacerSpan;
|
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
||||||
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||||
import org.joinmastodon.android.ui.views.SelectionListenerEditText;
|
import org.joinmastodon.android.ui.views.ComposeEditText;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -106,7 +100,7 @@ import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class ComposeFragment extends ToolbarFragment implements OnBackPressedListener, SelectionListenerEditText.SelectionListener{
|
public class ComposeFragment extends ToolbarFragment implements OnBackPressedListener, ComposeEditText.SelectionListener{
|
||||||
|
|
||||||
private static final int MEDIA_RESULT=717;
|
private static final int MEDIA_RESULT=717;
|
||||||
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
||||||
|
@ -142,7 +136,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
|
||||||
private Account self;
|
private Account self;
|
||||||
private String instanceDomain;
|
private String instanceDomain;
|
||||||
|
|
||||||
private SelectionListenerEditText mainEditText;
|
private ComposeEditText mainEditText;
|
||||||
private TextView charCounter;
|
private TextView charCounter;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private int charCount, charLimit, trimmedCharCount;
|
private int charCount, charLimit, trimmedCharCount;
|
||||||
|
@ -705,28 +699,31 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMediaAttachment(Uri uri){
|
private boolean addMediaAttachment(Uri uri){
|
||||||
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS){
|
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS){
|
||||||
showMediaAttachmentError(getResources().getQuantityString(R.plurals.cant_add_more_than_x_attachments, MAX_ATTACHMENTS, MAX_ATTACHMENTS));
|
showMediaAttachmentError(getResources().getQuantityString(R.plurals.cant_add_more_than_x_attachments, MAX_ATTACHMENTS, MAX_ATTACHMENTS));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
String type=getActivity().getContentResolver().getType(uri);
|
String type=getActivity().getContentResolver().getType(uri);
|
||||||
if(instance.configuration!=null && instance.configuration.mediaAttachments!=null){
|
if(instance.configuration!=null && instance.configuration.mediaAttachments!=null){
|
||||||
if(instance.configuration.mediaAttachments.supportedMimeTypes!=null && !instance.configuration.mediaAttachments.supportedMimeTypes.contains(type)){
|
if(instance.configuration.mediaAttachments.supportedMimeTypes!=null && !instance.configuration.mediaAttachments.supportedMimeTypes.contains(type)){
|
||||||
showMediaAttachmentError(getString(R.string.media_attachment_unsupported_type, UiUtils.getFileName(uri)));
|
showMediaAttachmentError(getString(R.string.media_attachment_unsupported_type, UiUtils.getFileName(uri)));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
int sizeLimit=type.startsWith("image/") ? instance.configuration.mediaAttachments.imageSizeLimit : instance.configuration.mediaAttachments.videoSizeLimit;
|
int sizeLimit=type.startsWith("image/") ? instance.configuration.mediaAttachments.imageSizeLimit : instance.configuration.mediaAttachments.videoSizeLimit;
|
||||||
int size;
|
int size;
|
||||||
try(Cursor cursor=MastodonApp.context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null)){
|
try(Cursor cursor=MastodonApp.context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null)){
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
size=cursor.getInt(0);
|
size=cursor.getInt(0);
|
||||||
|
}catch(Exception x){
|
||||||
|
Log.w("ComposeFragment", x);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if(size>sizeLimit){
|
if(size>sizeLimit){
|
||||||
float mb=sizeLimit/(float)(1024*1024);
|
float mb=sizeLimit/(float)(1024*1024);
|
||||||
String sMb=String.format(Locale.getDefault(), mb%1f==0f ? "%f" : "%.2f", mb);
|
String sMb=String.format(Locale.getDefault(), mb%1f==0f ? "%f" : "%.2f", mb);
|
||||||
showMediaAttachmentError(getString(R.string.media_attachment_too_big, UiUtils.getFileName(uri), sMb));
|
showMediaAttachmentError(getString(R.string.media_attachment_too_big, UiUtils.getFileName(uri), sMb));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pollBtn.setEnabled(false);
|
pollBtn.setEnabled(false);
|
||||||
|
@ -747,6 +744,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
|
||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS)
|
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS)
|
||||||
mediaBtn.setEnabled(false);
|
mediaBtn.setEnabled(false);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMediaAttachmentError(String text){
|
private void showMediaAttachmentError(String text){
|
||||||
|
@ -1073,6 +1071,18 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] onGetAllowedMediaMimeTypes(){
|
||||||
|
if(instance.configuration!=null && instance.configuration.mediaAttachments!=null && instance.configuration.mediaAttachments.supportedMimeTypes!=null)
|
||||||
|
return instance.configuration.mediaAttachments.supportedMimeTypes.toArray(new String[0]);
|
||||||
|
return new String[]{"image/jpeg", "image/gif", "image/png", "video/mp4"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onAddMediaAttachmentFromEditText(Uri uri){
|
||||||
|
return addMediaAttachment(uri);
|
||||||
|
}
|
||||||
|
|
||||||
private void startAutocomplete(ComposeAutocompleteSpan span){
|
private void startAutocomplete(ComposeAutocompleteSpan span){
|
||||||
currentAutocompleteSpan=span;
|
currentAutocompleteSpan=span;
|
||||||
Editable e=mainEditText.getText();
|
Editable e=mainEditText.getText();
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package org.joinmastodon.android.ui.views;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.DragEvent;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
import android.view.inputmethod.InputConnectionWrapper;
|
||||||
|
import android.view.inputmethod.InputContentInfo;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
public class ComposeEditText extends EditText{
|
||||||
|
private SelectionListener selectionListener;
|
||||||
|
private MediaAcceptingInputConnection inputConnectionWrapper=new MediaAcceptingInputConnection();
|
||||||
|
|
||||||
|
public ComposeEditText(Context context){
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComposeEditText(Context context, AttributeSet attrs){
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComposeEditText(Context context, AttributeSet attrs, int defStyleAttr){
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComposeEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSelectionChanged(int selStart, int selEnd){
|
||||||
|
super.onSelectionChanged(selStart, selEnd);
|
||||||
|
if(selectionListener!=null)
|
||||||
|
selectionListener.onSelectionChanged(selStart, selEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectionListener(SelectionListener selectionListener){
|
||||||
|
this.selectionListener=selectionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support receiving images from keyboards
|
||||||
|
@Override
|
||||||
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs){
|
||||||
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N_MR1){
|
||||||
|
outAttrs.contentMimeTypes=selectionListener.onGetAllowedMediaMimeTypes();
|
||||||
|
inputConnectionWrapper.setTarget(super.onCreateInputConnection(outAttrs));
|
||||||
|
return inputConnectionWrapper;
|
||||||
|
}
|
||||||
|
return super.onCreateInputConnection(outAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support pasting images
|
||||||
|
@Override
|
||||||
|
public boolean onTextContextMenuItem(int id){
|
||||||
|
if(id==android.R.id.paste){
|
||||||
|
ClipboardManager clipboard=getContext().getSystemService(ClipboardManager.class);
|
||||||
|
ClipData clip=clipboard.getPrimaryClip();
|
||||||
|
if(processClipData(clip))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onTextContextMenuItem(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support drag-and-dropping images in multiwindow mode
|
||||||
|
@Override
|
||||||
|
public boolean onDragEvent(DragEvent event){
|
||||||
|
if(event.getAction()==DragEvent.ACTION_DROP && Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
|
||||||
|
if(((Activity) getContext()).requestDragAndDropPermissions(event)!=null)
|
||||||
|
return processClipData(event.getClipData());
|
||||||
|
}
|
||||||
|
return super.onDragEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean processClipData(ClipData clip){
|
||||||
|
if(clip==null)
|
||||||
|
return false;
|
||||||
|
boolean processedAny=false;
|
||||||
|
for(int i=0;i<clip.getItemCount();i++){
|
||||||
|
Uri uri=clip.getItemAt(i).getUri();
|
||||||
|
if(uri!=null){
|
||||||
|
processedAny=true;
|
||||||
|
selectionListener.onAddMediaAttachmentFromEditText(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processedAny;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SelectionListener{
|
||||||
|
void onSelectionChanged(int start, int end);
|
||||||
|
String[] onGetAllowedMediaMimeTypes();
|
||||||
|
boolean onAddMediaAttachmentFromEditText(Uri uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MediaAcceptingInputConnection extends InputConnectionWrapper{
|
||||||
|
public MediaAcceptingInputConnection(){
|
||||||
|
super(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api=Build.VERSION_CODES.N_MR1)
|
||||||
|
@Override
|
||||||
|
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts){
|
||||||
|
Uri contentUri=inputContentInfo.getContentUri();
|
||||||
|
if(contentUri==null)
|
||||||
|
return false;
|
||||||
|
inputContentInfo.requestPermission();
|
||||||
|
return selectionListener.onAddMediaAttachmentFromEditText(contentUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
package org.joinmastodon.android.ui.views;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.EditText;
|
|
||||||
|
|
||||||
public class SelectionListenerEditText extends EditText{
|
|
||||||
private SelectionListener selectionListener;
|
|
||||||
|
|
||||||
public SelectionListenerEditText(Context context){
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectionListenerEditText(Context context, AttributeSet attrs){
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectionListenerEditText(Context context, AttributeSet attrs, int defStyleAttr){
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectionListenerEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSelectionChanged(int selStart, int selEnd){
|
|
||||||
super.onSelectionChanged(selStart, selEnd);
|
|
||||||
if(selectionListener!=null)
|
|
||||||
selectionListener.onSelectionChanged(selStart, selEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectionListener(SelectionListener selectionListener){
|
|
||||||
this.selectionListener=selectionListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface SelectionListener{
|
|
||||||
void onSelectionChanged(int start, int end);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -89,7 +89,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp">
|
android:layout_marginTop="10dp">
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.SelectionListenerEditText
|
<org.joinmastodon.android.ui.views.ComposeEditText
|
||||||
android:id="@+id/toot_text"
|
android:id="@+id/toot_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
Loading…
Reference in New Issue