mirror of
				https://codeberg.org/tom79/Fedilab.git
				synced 2025-10-20 11:20:16 +03:00 
			
		
		
		
	Fix issue #60 - Allow to upload gif from keyboard
This commit is contained in:
		
							parent
							
								
									bb6e31c00a
								
							
						
					
					
						commit
						5f8685db32
					
				
					 4 changed files with 241 additions and 2 deletions
				
			
		|  | @ -0,0 +1,198 @@ | ||||||
|  | package app.fedilab.android.helper; | ||||||
|  | 
 | ||||||
|  | import static app.fedilab.android.ui.drawer.ComposeAdapter.autocomplete; | ||||||
|  | 
 | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.SharedPreferences; | ||||||
|  | import android.content.res.TypedArray; | ||||||
|  | import android.graphics.Paint; | ||||||
|  | import android.os.Build; | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.util.AttributeSet; | ||||||
|  | import android.view.KeyEvent; | ||||||
|  | import android.view.inputmethod.EditorInfo; | ||||||
|  | import android.view.inputmethod.InputConnection; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.CallSuper; | ||||||
|  | import androidx.annotation.DimenRes; | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.annotation.Px; | ||||||
|  | import androidx.core.view.inputmethod.EditorInfoCompat; | ||||||
|  | import androidx.core.view.inputmethod.InputConnectionCompat; | ||||||
|  | import androidx.core.view.inputmethod.InputContentInfoCompat; | ||||||
|  | import androidx.preference.PreferenceManager; | ||||||
|  | 
 | ||||||
|  | import com.google.android.material.textfield.MaterialAutoCompleteTextView; | ||||||
|  | import com.vanniktech.emoji.EmojiManager; | ||||||
|  | import com.vanniktech.emoji.emoji.Emoji; | ||||||
|  | 
 | ||||||
|  | import app.fedilab.android.R; | ||||||
|  | import app.fedilab.android.interfaces.EmojiEditTextInterface; | ||||||
|  | 
 | ||||||
|  | public class FedilabAutoCompleteTextView extends MaterialAutoCompleteTextView implements EmojiEditTextInterface { | ||||||
|  | 
 | ||||||
|  |     private float emojiSize; | ||||||
|  |     private boolean emoji; | ||||||
|  |     private String[] imgTypeString; | ||||||
|  |     private KeyBoardInputCallbackListener keyBoardInputCallbackListener; | ||||||
|  |     final InputConnectionCompat.OnCommitContentListener callback = | ||||||
|  |             new InputConnectionCompat.OnCommitContentListener() { | ||||||
|  |                 @Override | ||||||
|  |                 public boolean onCommitContent(@NonNull InputContentInfoCompat inputContentInfo, | ||||||
|  |                                                int flags, Bundle opts) { | ||||||
|  | 
 | ||||||
|  |                     // read and display inputContentInfo asynchronously | ||||||
|  |                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && (flags & | ||||||
|  |                             InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) { | ||||||
|  |                         try { | ||||||
|  |                             inputContentInfo.requestPermission(); | ||||||
|  |                         } catch (Exception e) { | ||||||
|  |                             return false; // return false if failed | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     boolean supported = false; | ||||||
|  |                     for (final String mimeType : imgTypeString) { | ||||||
|  |                         if (inputContentInfo.getDescription().hasMimeType(mimeType)) { | ||||||
|  |                             supported = true; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (!supported) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (keyBoardInputCallbackListener != null) { | ||||||
|  |                         keyBoardInputCallbackListener.onCommitContent(inputContentInfo, flags, opts); | ||||||
|  |                     } | ||||||
|  |                     return true;  // return true if succeeded | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public FedilabAutoCompleteTextView(Context context) { | ||||||
|  |         super(context); | ||||||
|  |         initView(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FedilabAutoCompleteTextView(Context context, AttributeSet attrs) { | ||||||
|  |         super(context, attrs); | ||||||
|  | 
 | ||||||
|  |         final Paint.FontMetrics fontMetrics = getPaint().getFontMetrics(); | ||||||
|  |         final float defaultEmojiSize = fontMetrics.descent - fontMetrics.ascent; | ||||||
|  |         final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); | ||||||
|  |         emoji = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_EMOJI), false); | ||||||
|  |         if (attrs == null) { | ||||||
|  |             emojiSize = defaultEmojiSize; | ||||||
|  |         } else { | ||||||
|  |             final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.EmojiMultiAutoCompleteTextView); | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 emojiSize = a.getDimension(R.styleable.EmojiMultiAutoCompleteTextView_emojiSize, defaultEmojiSize); | ||||||
|  |             } finally { | ||||||
|  |                 a.recycle(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         setText(getText()); | ||||||
|  |         initView(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FedilabAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) { | ||||||
|  |         super(context, attrs, defStyleAttr); | ||||||
|  |         initView(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public InputConnection onCreateInputConnection(EditorInfo outAttrs) { | ||||||
|  |         final InputConnection ic = super.onCreateInputConnection(outAttrs); | ||||||
|  |         EditorInfoCompat.setContentMimeTypes(outAttrs, | ||||||
|  |                 imgTypeString); | ||||||
|  |         return InputConnectionCompat.createWrapper(ic, outAttrs, callback); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void initView() { | ||||||
|  |         imgTypeString = new String[]{"image/png", | ||||||
|  |                 "image/gif", | ||||||
|  |                 "image/jpeg", | ||||||
|  |                 "image/webp"}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setKeyBoardInputCallbackListener(KeyBoardInputCallbackListener keyBoardInputCallbackListener) { | ||||||
|  |         this.keyBoardInputCallbackListener = keyBoardInputCallbackListener; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @SuppressWarnings("unused") | ||||||
|  |     public String[] getImgTypeString() { | ||||||
|  |         return imgTypeString; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @SuppressWarnings("unused") | ||||||
|  |     public void setImgTypeString(String[] imgTypeString) { | ||||||
|  |         this.imgTypeString = imgTypeString; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     @CallSuper | ||||||
|  |     protected void onTextChanged(final CharSequence text, final int start, final int lengthBefore, final int lengthAfter) { | ||||||
|  |         final Paint.FontMetrics fontMetrics = getPaint().getFontMetrics(); | ||||||
|  |         final float defaultEmojiSize = fontMetrics.descent - fontMetrics.ascent; | ||||||
|  |         if (emoji && !autocomplete) { | ||||||
|  |             EmojiManager.getInstance().replaceWithImages(getContext(), getText(), emojiSize, defaultEmojiSize); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void backspace() { | ||||||
|  |         final KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL); | ||||||
|  |         dispatchKeyEvent(event); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public float getEmojiSize() { | ||||||
|  |         return emojiSize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public final void setEmojiSize(@Px final int pixels) { | ||||||
|  |         setEmojiSize(pixels, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     @CallSuper | ||||||
|  |     public void input(final Emoji emoji) { | ||||||
|  |         if (emoji != null && !autocomplete) { | ||||||
|  |             final int start = getSelectionStart(); | ||||||
|  |             final int end = getSelectionEnd(); | ||||||
|  | 
 | ||||||
|  |             if (start < 0) { | ||||||
|  |                 append(emoji.getUnicode()); | ||||||
|  |             } else { | ||||||
|  |                 getText().replace(Math.min(start, end), Math.max(start, end), emoji.getUnicode(), 0, emoji.getUnicode().length()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public final void setEmojiSize(@Px final int pixels, final boolean shouldInvalidate) { | ||||||
|  |         emojiSize = pixels; | ||||||
|  | 
 | ||||||
|  |         if (shouldInvalidate && !autocomplete) { | ||||||
|  |             setText(getText()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public final void setEmojiSizeRes(@DimenRes final int res) { | ||||||
|  |         setEmojiSizeRes(res, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public final void setEmojiSizeRes(@DimenRes final int res, final boolean shouldInvalidate) { | ||||||
|  |         setEmojiSize(getResources().getDimensionPixelSize(res), shouldInvalidate); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public interface KeyBoardInputCallbackListener { | ||||||
|  |         void onCommitContent(InputContentInfoCompat inputContentInfo, | ||||||
|  |                              int flags, Bundle opts); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | package app.fedilab.android.interfaces; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.DimenRes; | ||||||
|  | import androidx.annotation.Px; | ||||||
|  | 
 | ||||||
|  | import com.vanniktech.emoji.emoji.Emoji; | ||||||
|  | 
 | ||||||
|  | public interface EmojiEditTextInterface { | ||||||
|  |     void backspace(); | ||||||
|  | 
 | ||||||
|  |     void input(Emoji emoji); | ||||||
|  | 
 | ||||||
|  |     float getEmojiSize(); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * sets the emoji size in pixels and automatically invalidates the text and renders it with the new size | ||||||
|  |      */ | ||||||
|  |     void setEmojiSize(@Px int pixels); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * sets the emoji size in pixels and automatically invalidates the text and renders it with the new size when {@code shouldInvalidate} is true | ||||||
|  |      */ | ||||||
|  |     void setEmojiSize(@Px int pixels, boolean shouldInvalidate); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * sets the emoji size in pixels with the provided resource and automatically invalidates the text and renders it with the new size | ||||||
|  |      */ | ||||||
|  |     void setEmojiSizeRes(@DimenRes int res); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * sets the emoji size in pixels with the provided resource and invalidates the text and renders it with the new size when {@code shouldInvalidate} is true | ||||||
|  |      */ | ||||||
|  |     void setEmojiSizeRes(@DimenRes int res, boolean shouldInvalidate); | ||||||
|  | } | ||||||
|  | @ -1135,7 +1135,14 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder | ||||||
|                     statusDraft.text = new SpannableString(Html.fromHtml(statusDraft.content)).toString(); |                     statusDraft.text = new SpannableString(Html.fromHtml(statusDraft.content)).toString(); | ||||||
|             } |             } | ||||||
|             holder.binding.content.setText(statusDraft.text); |             holder.binding.content.setText(statusDraft.text); | ||||||
| 
 |             holder.binding.content.setKeyBoardInputCallbackListener((inputContentInfo, flags, opts) -> { | ||||||
|  |                 if (inputContentInfo != null) { | ||||||
|  |                     Uri uri = inputContentInfo.getContentUri(); | ||||||
|  |                     List<Uri> uris = new ArrayList<>(); | ||||||
|  |                     uris.add(uri); | ||||||
|  |                     addAttachment(position, uris); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|             holder.binding.content.setSelection(statusDraft.cursorPosition); |             holder.binding.content.setSelection(statusDraft.cursorPosition); | ||||||
|             if (statusDraft.setCursorToEnd) { |             if (statusDraft.setCursorToEnd) { | ||||||
|                 statusDraft.setCursorToEnd = false; |                 statusDraft.setCursorToEnd = false; | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ | ||||||
|             android:visibility="gone" |             android:visibility="gone" | ||||||
|             app:layout_constraintTop_toBottomOf="@id/add_remove_status" /> |             app:layout_constraintTop_toBottomOf="@id/add_remove_status" /> | ||||||
| 
 | 
 | ||||||
|         <com.google.android.material.textfield.MaterialAutoCompleteTextView |         <app.fedilab.android.helper.FedilabAutoCompleteTextView | ||||||
|             android:id="@+id/content" |             android:id="@+id/content" | ||||||
|             android:layout_width="match_parent" |             android:layout_width="match_parent" | ||||||
|             android:layout_height="wrap_content" |             android:layout_height="wrap_content" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue