From 962a59ac4f1d1a0279eae050cd6d339a57b7807c Mon Sep 17 00:00:00 2001
From: Thomas <tschneider.ac@gmail.com>
Date: Wed, 16 Nov 2022 17:41:47 +0100
Subject: [PATCH] Fix issue #449 - Fix draft issues

---
 .../android/activities/ComposeActivity.java   |  52 +-
 .../android/ui/drawer/ComposeAdapter.java     | 728 +++++++++---------
 2 files changed, 409 insertions(+), 371 deletions(-)

diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java
index 1dae4508..06f2ff41 100644
--- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java
+++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java
@@ -97,7 +97,7 @@ import app.fedilab.android.viewmodel.mastodon.AccountsVM;
 import app.fedilab.android.viewmodel.mastodon.StatusesVM;
 import es.dmoral.toasty.Toasty;
 
-public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone {
+public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone, ComposeAdapter.promptDraftListener {
 
 
     public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754;
@@ -109,6 +109,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
     private Status statusReply, statusMention;
     private StatusDraft statusDraft;
     private ComposeAdapter composeAdapter;
+    private boolean promptSaveDraft;
 
 
     private final BroadcastReceiver imageReceiver = new BroadcastReceiver() {
@@ -213,25 +214,34 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
             statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
         }
         if (canBeSent(statusDraft)) {
-            AlertDialog.Builder alt_bld = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
-            alt_bld.setMessage(R.string.save_draft);
-            alt_bld.setPositiveButton(R.string.save, (dialog, id) -> {
-                dialog.dismiss();
-                storeDraft(false);
-                finish();
+            if (promptSaveDraft) {
+                AlertDialog.Builder alt_bld = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
+                alt_bld.setMessage(R.string.save_draft);
+                alt_bld.setPositiveButton(R.string.save, (dialog, id) -> {
+                    dialog.dismiss();
+                    storeDraft(false);
+                    finish();
 
-            });
-            alt_bld.setNegativeButton(R.string.no, (dialog, id) -> {
+                });
+                alt_bld.setNegativeButton(R.string.no, (dialog, id) -> {
+                    try {
+                        new StatusDraft(ComposeActivity.this).removeDraft(statusDraft);
+                    } catch (DBException e) {
+                        e.printStackTrace();
+                    }
+                    dialog.dismiss();
+                    finish();
+                });
+                AlertDialog alert = alt_bld.create();
+                alert.show();
+            } else {
                 try {
                     new StatusDraft(ComposeActivity.this).removeDraft(statusDraft);
+                    finish();
                 } catch (DBException e) {
                     e.printStackTrace();
                 }
-                dialog.dismiss();
-                finish();
-            });
-            AlertDialog alert = alt_bld.create();
-            alert.show();
+            }
         } else {
             finish();
         }
@@ -278,6 +288,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
         statusList.add(initialStatus);
         statusList.add(statusDraft.statusDraftList.get(0));
         composeAdapter = new ComposeAdapter(statusList, context.ancestors.size(), account, accountMention, visibility, editMessageId);
+        composeAdapter.promptDraftListener = this;
         composeAdapter.manageDrafts = this;
         LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
         binding.recyclerView.setLayoutManager(mLayoutManager);
@@ -434,6 +445,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
         binding = ActivityPaginationBinding.inflate(getLayoutInflater());
         setContentView(binding.getRoot());
         setSupportActionBar(binding.toolbar);
+        promptSaveDraft = false;
         ActionBar actionBar = getSupportActionBar();
         //Remove title
         if (actionBar != null) {
@@ -572,6 +584,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
             statusList.addAll(statusDraft.statusDraftList);
             composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
             composeAdapter.manageDrafts = this;
+            composeAdapter.promptDraftListener = this;
             LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
             binding.recyclerView.setLayoutManager(mLayoutManager);
             binding.recyclerView.setAdapter(composeAdapter);
@@ -627,6 +640,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
             statusList.addAll(statusDraftList);
             composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
             composeAdapter.manageDrafts = this;
+            composeAdapter.promptDraftListener = this;
             LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
             binding.recyclerView.setLayoutManager(mLayoutManager);
             binding.recyclerView.setAdapter(composeAdapter);
@@ -637,6 +651,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
             statusList.addAll(statusDraftList);
             composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility, editMessageId);
             composeAdapter.manageDrafts = this;
+            composeAdapter.promptDraftListener = this;
             LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
             binding.recyclerView.setLayoutManager(mLayoutManager);
             binding.recyclerView.setAdapter(composeAdapter);
@@ -653,7 +668,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
             timer.scheduleAtFixedRate(new TimerTask() {
                 @Override
                 public void run() {
-                    storeDraft(false);
+                    if (promptSaveDraft) {
+                        storeDraft(false);
+                    }
                 }
             }, 0, 10000);
         }
@@ -872,6 +889,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
         composeAdapter.updateContent(isChecked, acct);
     }
 
+    @Override
+    public void promptDraft() {
+        promptSaveDraft = true;
+    }
+
 
     public enum mediaType {
         PHOTO,
diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java
index aff3d0a0..17bb34d8 100644
--- a/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java
+++ b/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java
@@ -142,6 +142,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
     private Context context;
     private AlertDialog alertDialogEmoji;
     private List<Emoji> emojisList = new ArrayList<>();
+    public promptDraftListener promptDraftListener;
 
     public ComposeAdapter(List<Status> statusList, int statusCount, BaseAccount account, app.fedilab.android.client.entities.api.Account mentionedAccount, String visibility, String editMessageId) {
         this.statusList = statusList;
@@ -153,6 +154,30 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
 
     }
 
+    /**
+     * Add an attachment from ComposeActivity
+     *
+     * @param position int - position of the drawer that added a media
+     * @param uris     List<Uri> - uris of the media
+     */
+    public void addAttachment(int position, List<Uri> uris) {
+        if (position == -1) {
+            position = statusList.size() - 1;
+        }
+        // position = statusCount-1+position;
+        if (statusList.get(position).media_attachments == null) {
+            statusList.get(position).media_attachments = new ArrayList<>();
+        }
+        if (promptDraftListener != null) {
+            promptDraftListener.promptDraft();
+        }
+        int finalPosition = position;
+        Helper.createAttachmentFromUri(context, uris, attachment -> {
+            statusList.get(finalPosition).media_attachments.add(attachment);
+            notifyItemChanged(finalPosition);
+        });
+    }
+
     private static void updateCharacterCount(ComposeViewHolder composeViewHolder) {
         int charCount = MastodonHelper.countLength(composeViewHolder);
         composeViewHolder.binding.characterCount.setText(String.valueOf(charCount));
@@ -391,327 +416,6 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
                 && (draft.spoiler_text == null || draft.spoiler_text.trim().length() == 0);
     }
 
-    /**
-     * Add an attachment from ComposeActivity
-     *
-     * @param position int - position of the drawer that added a media
-     * @param uris     List<Uri> - uris of the media
-     */
-    public void addAttachment(int position, List<Uri> uris) {
-        if (position == -1) {
-            position = statusList.size() - 1;
-        }
-        // position = statusCount-1+position;
-        if (statusList.get(position).media_attachments == null) {
-            statusList.get(position).media_attachments = new ArrayList<>();
-        }
-        int finalPosition = position;
-        Helper.createAttachmentFromUri(context, uris, attachment -> {
-            statusList.get(finalPosition).media_attachments.add(attachment);
-            notifyItemChanged(finalPosition);
-        });
-    }
-
-    /**
-     * Add a shared element
-     * If title and description are empty, it will use subject and content coming from the intent
-     *
-     * @param url         - String url that is shared
-     * @param title       - String title gather from the URL
-     * @param description - String description gathered from the URL
-     * @param subject     - String subject (title) comming from the shared elements
-     * @param content     - String content (description) coming from the shared elements
-     */
-    public void addSharing(String url, String title, String description, String subject, String content, String saveFilePath) {
-
-        if (description == null && content == null) {
-            return;
-        }
-        int position = statusList.size() - 1;
-        statusList.get(position).text = "";
-        if (title != null && title.trim().length() > 0) {
-            statusList.get(position).text += title + "\n\n";
-        } else if (subject != null && subject.trim().length() > 0) {
-            statusList.get(position).text += subject + "\n\n";
-        }
-        if (description != null && description.trim().length() > 0) {
-            statusList.get(position).text += description + "\n\n";
-            if (url != null && !description.contains(url)) {
-                statusList.get(position).text += url;
-            }
-        } else if (content != null && content.trim().length() > 0) {
-            statusList.get(position).text += content + "\n\n";
-            if (!content.contains(url)) {
-                statusList.get(position).text += url;
-            }
-        } else {
-            statusList.get(position).text += url;
-        }
-        if (saveFilePath != null) {
-            Attachment attachment = new Attachment();
-            attachment.mimeType = "image/*";
-            String extension = "jpg";
-            SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_", Locale.getDefault());
-            attachment.local_path = saveFilePath;
-            Date now = new Date();
-            attachment.filename = formatter.format(now) + "." + extension;
-            if (statusList.get(position).media_attachments == null) {
-                statusList.get(position).media_attachments = new ArrayList<>();
-            }
-            statusList.get(position).media_attachments.add(attachment);
-        }
-        notifyItemChanged(position);
-    }
-
-    //<------ Manage contact from compose activity
-    //It only targets last message in a thread
-    //Return content of last compose message
-    public String getLastComposeContent() {
-        return statusList.get(statusList.size() - 1).text != null ? statusList.get(statusList.size() - 1).text : "";
-    }
-    //------- end contact ----->
-
-    //Used to write contact when composing
-    public void updateContent(boolean checked, String acct) {
-        if (checked) {
-            if (!statusList.get(statusList.size() - 1).text.contains(acct))
-                statusList.get(statusList.size() - 1).text = String.format("%s %s", acct, statusList.get(statusList.size() - 1).text);
-        } else {
-            statusList.get(statusList.size() - 1).text = statusList.get(statusList.size() - 1).text.replaceAll("\\s*" + acct, "");
-        }
-        notifyItemChanged(statusList.size() - 1);
-    }
-
-    //Put cursor to the end after changing contacts
-    public void putCursor() {
-        statusList.get(statusList.size() - 1).setCursorToEnd = true;
-        notifyItemChanged(statusList.size() - 1);
-    }
-
-    /**
-     * Display attachment for a holder
-     *
-     * @param holder                - view related to a compose element {@link ComposeViewHolder}
-     * @param position              - int position of the message in the thread
-     * @param scrollToMediaPosition - int the position to scroll to media
-     */
-    private void displayAttachments(ComposeViewHolder holder, int position, int scrollToMediaPosition) {
-        if (statusList.size() > position && statusList.get(position).media_attachments != null) {
-            holder.binding.attachmentsList.removeAllViews();
-            List<Attachment> attachmentList = statusList.get(position).media_attachments;
-            if (attachmentList != null && attachmentList.size() > 0) {
-                holder.binding.sensitiveMedia.setVisibility(View.VISIBLE);
-                if (!statusList.get(position).sensitive) {
-                    if (currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) {
-                        holder.binding.sensitiveMedia.setChecked(currentAccount.mastodon_account.source.sensitive);
-                        statusList.get(position).sensitive = currentAccount.mastodon_account.source.sensitive;
-                    } else {
-                        statusList.get(position).sensitive = false;
-                    }
-                }
-
-                holder.binding.sensitiveMedia.setOnCheckedChangeListener((buttonView, isChecked) -> statusList.get(position).sensitive = isChecked);
-                int mediaPosition = 0;
-                for (Attachment attachment : attachmentList) {
-                    ComposeAttachmentItemBinding composeAttachmentItemBinding = ComposeAttachmentItemBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false);
-                    composeAttachmentItemBinding.buttonPlay.setVisibility(View.GONE);
-                    String attachmentPath = attachment.local_path != null && !attachment.local_path.trim().isEmpty() ? attachment.local_path : attachment.preview_url;
-                    if (attachment.type != null || attachment.mimeType != null) {
-                        if ((attachment.type != null && attachment.type.toLowerCase().startsWith("image")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("image"))) {
-                            Glide.with(composeAttachmentItemBinding.preview.getContext())
-                                    .load(attachmentPath)
-                                    .diskCacheStrategy(DiskCacheStrategy.NONE)
-                                    .skipMemoryCache(true)
-                                    .into(composeAttachmentItemBinding.preview);
-                        } else if ((attachment.type != null && attachment.type.toLowerCase().startsWith("video")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("video"))) {
-                            composeAttachmentItemBinding.buttonPlay.setVisibility(View.VISIBLE);
-                            long interval = 2000;
-                            RequestOptions options = new RequestOptions().frame(interval);
-                            Glide.with(composeAttachmentItemBinding.preview.getContext()).asBitmap()
-                                    .load(attachmentPath)
-                                    .apply(options)
-                                    .into(composeAttachmentItemBinding.preview);
-                        } else if ((attachment.type != null && attachment.type.toLowerCase().startsWith("audio")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("audio"))) {
-                            Glide.with(composeAttachmentItemBinding.preview.getContext())
-                                    .load(R.drawable.ic_baseline_audio_file_24)
-                                    .into(composeAttachmentItemBinding.preview);
-                        } else {
-                            Glide.with(composeAttachmentItemBinding.preview.getContext())
-                                    .load(R.drawable.ic_baseline_insert_drive_file_24)
-                                    .into(composeAttachmentItemBinding.preview);
-                        }
-                    } else {
-                        Glide.with(composeAttachmentItemBinding.preview.getContext())
-                                .load(R.drawable.ic_baseline_insert_drive_file_24)
-                                .into(composeAttachmentItemBinding.preview);
-                    }
-                    if (mediaPosition == 0) {
-                        composeAttachmentItemBinding.buttonOrderUp.setVisibility(View.INVISIBLE);
-                    } else {
-                        composeAttachmentItemBinding.buttonOrderUp.setVisibility(View.VISIBLE);
-                    }
-                    if (mediaPosition == attachmentList.size() - 1) {
-                        composeAttachmentItemBinding.buttonOrderDown.setVisibility(View.INVISIBLE);
-                    } else {
-                        composeAttachmentItemBinding.buttonOrderDown.setVisibility(View.VISIBLE);
-                    }
-                    //Remote attachments when deleting/redrafting can't be ordered
-                    if (attachment.local_path == null) {
-                        composeAttachmentItemBinding.buttonOrderUp.setVisibility(View.INVISIBLE);
-                        composeAttachmentItemBinding.buttonOrderDown.setVisibility(View.INVISIBLE);
-                    }
-                    int finalMediaPosition = mediaPosition;
-                    composeAttachmentItemBinding.editPreview.setOnClickListener(v -> {
-                        Intent intent = new Intent(context, EditImageActivity.class);
-                        Bundle b = new Bundle();
-                        intent.putExtra("imageUri", attachment.local_path);
-                        intent.putExtras(b);
-                        context.startActivity(intent);
-                    });
-                    composeAttachmentItemBinding.buttonDescription.setOnClickListener(v -> {
-                        AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle());
-                        builderInner.setTitle(R.string.upload_form_description);
-                        PopupMediaDescriptionBinding popupMediaDescriptionBinding = PopupMediaDescriptionBinding.inflate(LayoutInflater.from(context), null, false);
-                        builderInner.setView(popupMediaDescriptionBinding.getRoot());
-
-                        popupMediaDescriptionBinding.mediaDescription.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1500)});
-                        popupMediaDescriptionBinding.mediaDescription.requestFocus();
-                        Glide.with(popupMediaDescriptionBinding.mediaPicture.getContext())
-                                .asBitmap()
-                                .load(attachmentPath)
-                                .into(new CustomTarget<Bitmap>() {
-                                    @Override
-                                    public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
-                                        popupMediaDescriptionBinding.mediaPicture.setImageBitmap(resource);
-                                    }
-
-                                    @Override
-                                    public void onLoadCleared(@Nullable Drawable placeholder) {
-
-                                    }
-                                });
-                        builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
-                        if (attachment.description != null) {
-                            popupMediaDescriptionBinding.mediaDescription.setText(attachment.description);
-                            popupMediaDescriptionBinding.mediaDescription.setSelection(popupMediaDescriptionBinding.mediaDescription.getText().length());
-                        }
-                        builderInner.setPositiveButton(R.string.validate, (dialog, which) -> {
-                            attachment.description = popupMediaDescriptionBinding.mediaDescription.getText().toString();
-                            displayAttachments(holder, position, finalMediaPosition);
-                            dialog.dismiss();
-                        });
-                        AlertDialog alertDialog = builderInner.create();
-                        Objects.requireNonNull(alertDialog.getWindow()).setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-                        alertDialog.show();
-                        popupMediaDescriptionBinding.mediaDescription.requestFocus();
-
-                    });
-
-                    composeAttachmentItemBinding.buttonOrderUp.setOnClickListener(v -> {
-                        if (finalMediaPosition > 0 && attachmentList.size() > 1) {
-                            Attachment at1 = attachmentList.get(finalMediaPosition);
-                            Attachment at2 = attachmentList.get(finalMediaPosition - 1);
-                            attachmentList.set(finalMediaPosition - 1, at1);
-                            attachmentList.set(finalMediaPosition, at2);
-                            displayAttachments(holder, position, finalMediaPosition - 1);
-                        }
-                    });
-                    composeAttachmentItemBinding.buttonOrderDown.setOnClickListener(v -> {
-                        if (finalMediaPosition < (attachmentList.size() - 1) && attachmentList.size() > 1) {
-                            Attachment at1 = attachmentList.get(finalMediaPosition);
-                            Attachment at2 = attachmentList.get(finalMediaPosition + 1);
-                            attachmentList.set(finalMediaPosition, at2);
-                            attachmentList.set(finalMediaPosition + 1, at1);
-                            displayAttachments(holder, position, finalMediaPosition + 1);
-                        }
-                    });
-                    composeAttachmentItemBinding.buttonRemove.setOnClickListener(v -> {
-                        AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle());
-                        builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
-                        builderInner.setPositiveButton(R.string.delete, (dialog, which) -> {
-                            attachmentList.remove(attachment);
-                            displayAttachments(holder, position, finalMediaPosition);
-                            new Thread(() -> {
-                                if (attachment.local_path != null) {
-                                    File fileToDelete = new File(attachment.local_path);
-                                    if (fileToDelete.exists()) {
-                                        //noinspection ResultOfMethodCallIgnored
-                                        fileToDelete.delete();
-                                    }
-                                }
-                            }).start();
-
-                        });
-                        builderInner.setMessage(R.string.toot_delete_media);
-                        builderInner.show();
-                    });
-                    composeAttachmentItemBinding.preview.setOnClickListener(v -> displayAttachments(holder, position, finalMediaPosition));
-                    if (attachment.description == null || attachment.description.trim().isEmpty()) {
-                        composeAttachmentItemBinding.buttonDescription.setIconResource(R.drawable.ic_baseline_warning_24);
-                        composeAttachmentItemBinding.buttonDescription.setStrokeColor(ThemeHelper.getNoDescriptionColorStateList(context));
-                        composeAttachmentItemBinding.buttonDescription.setTextColor(ContextCompat.getColor(context, R.color.no_description));
-                        Helper.changeDrawableColor(context, R.drawable.ic_baseline_warning_24, ContextCompat.getColor(context, R.color.no_description));
-                        composeAttachmentItemBinding.buttonDescription.setIconTint(ThemeHelper.getNoDescriptionColorStateList(context));
-                    } else {
-                        composeAttachmentItemBinding.buttonDescription.setIconTint(ThemeHelper.getHavingDescriptionColorStateList(context));
-                        composeAttachmentItemBinding.buttonDescription.setIconResource(R.drawable.ic_baseline_check_circle_24);
-                        composeAttachmentItemBinding.buttonDescription.setTextColor(ContextCompat.getColor(context, R.color.having_description));
-                        composeAttachmentItemBinding.buttonDescription.setStrokeColor(ThemeHelper.getHavingDescriptionColorStateList(context));
-                        Helper.changeDrawableColor(context, R.drawable.ic_baseline_check_circle_24, ContextCompat.getColor(context, R.color.having_description));
-                    }
-                    holder.binding.attachmentsList.addView(composeAttachmentItemBinding.getRoot());
-                    mediaPosition++;
-                }
-                holder.binding.attachmentsList.setVisibility(View.VISIBLE);
-                if (scrollToMediaPosition >= 0 && holder.binding.attachmentsList.getChildCount() < scrollToMediaPosition) {
-                    holder.binding.attachmentsList.requestChildFocus(holder.binding.attachmentsList.getChildAt(scrollToMediaPosition), holder.binding.attachmentsList.getChildAt(scrollToMediaPosition));
-                }
-            } else {
-                holder.binding.attachmentsList.setVisibility(View.GONE);
-                holder.binding.sensitiveMedia.setVisibility(View.GONE);
-            }
-        } else {
-            holder.binding.attachmentsList.setVisibility(View.GONE);
-            holder.binding.sensitiveMedia.setVisibility(View.GONE);
-        }
-        buttonState(holder);
-    }
-
-    /**
-     * Manage state of media and poll button
-     *
-     * @param holder ComposeViewHolder
-     */
-    private void buttonState(ComposeViewHolder holder) {
-        if (BaseMainActivity.software == null || BaseMainActivity.software.toUpperCase().compareTo("MASTODON") == 0) {
-            if (holder.getBindingAdapterPosition() > 0) {
-                Status statusDraft = statusList.get(holder.getBindingAdapterPosition());
-                if (statusDraft.poll == null) {
-                    holder.binding.buttonAttachImage.setEnabled(true);
-                    holder.binding.buttonAttachVideo.setEnabled(true);
-                    holder.binding.buttonAttachAudio.setEnabled(true);
-                    holder.binding.buttonAttachManual.setEnabled(true);
-                } else {
-                    holder.binding.buttonAttachImage.setEnabled(false);
-                    holder.binding.buttonAttachVideo.setEnabled(false);
-                    holder.binding.buttonAttachAudio.setEnabled(false);
-                    holder.binding.buttonAttachManual.setEnabled(false);
-                    holder.binding.buttonPoll.setEnabled(true);
-                }
-                holder.binding.buttonPoll.setEnabled(statusDraft.media_attachments == null || statusDraft.media_attachments.size() <= 0);
-            }
-        }
-    }
-
-    public long getItemId(int position) {
-        return position;
-    }
-
-    @Override
-    public int getItemCount() {
-        return statusList.size();
-    }
-
     /**
      * Initialize text watcher for content writing
      * It will allow to complete autocomplete edit text while starting words with @, #, : etc.
@@ -747,6 +451,9 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
             @Override
             public void afterTextChanged(Editable s) {
                 int currentLength = MastodonHelper.countLength(holder);
+                if (promptDraftListener != null) {
+                    promptDraftListener.promptDraft();
+                }
                 statusList.get(holder.getLayoutPosition()).cursorPosition = holder.binding.content.getSelectionStart();
                 //Copy/past
                 int max_car = MastodonHelper.getInstanceMaxChars(context);
@@ -1060,6 +767,306 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
         return textw;
     }
 
+    /**
+     * Add a shared element
+     * If title and description are empty, it will use subject and content coming from the intent
+     *
+     * @param url         - String url that is shared
+     * @param title       - String title gather from the URL
+     * @param description - String description gathered from the URL
+     * @param subject     - String subject (title) comming from the shared elements
+     * @param content     - String content (description) coming from the shared elements
+     */
+    public void addSharing(String url, String title, String description, String subject, String content, String saveFilePath) {
+
+        if (description == null && content == null) {
+            return;
+        }
+        int position = statusList.size() - 1;
+        statusList.get(position).text = "";
+        if (title != null && title.trim().length() > 0) {
+            statusList.get(position).text += title + "\n\n";
+        } else if (subject != null && subject.trim().length() > 0) {
+            statusList.get(position).text += subject + "\n\n";
+        }
+        if (description != null && description.trim().length() > 0) {
+            statusList.get(position).text += description + "\n\n";
+            if (url != null && !description.contains(url)) {
+                statusList.get(position).text += url;
+            }
+        } else if (content != null && content.trim().length() > 0) {
+            statusList.get(position).text += content + "\n\n";
+            if (!content.contains(url)) {
+                statusList.get(position).text += url;
+            }
+        } else {
+            statusList.get(position).text += url;
+        }
+        if (saveFilePath != null) {
+            Attachment attachment = new Attachment();
+            attachment.mimeType = "image/*";
+            String extension = "jpg";
+            SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_", Locale.getDefault());
+            attachment.local_path = saveFilePath;
+            Date now = new Date();
+            attachment.filename = formatter.format(now) + "." + extension;
+            if (statusList.get(position).media_attachments == null) {
+                statusList.get(position).media_attachments = new ArrayList<>();
+            }
+            statusList.get(position).media_attachments.add(attachment);
+        }
+        notifyItemChanged(position);
+    }
+
+    //<------ Manage contact from compose activity
+    //It only targets last message in a thread
+    //Return content of last compose message
+    public String getLastComposeContent() {
+        return statusList.get(statusList.size() - 1).text != null ? statusList.get(statusList.size() - 1).text : "";
+    }
+    //------- end contact ----->
+
+    //Used to write contact when composing
+    public void updateContent(boolean checked, String acct) {
+        if (checked) {
+            if (!statusList.get(statusList.size() - 1).text.contains(acct))
+                statusList.get(statusList.size() - 1).text = String.format("%s %s", acct, statusList.get(statusList.size() - 1).text);
+        } else {
+            statusList.get(statusList.size() - 1).text = statusList.get(statusList.size() - 1).text.replaceAll("\\s*" + acct, "");
+        }
+        notifyItemChanged(statusList.size() - 1);
+    }
+
+    //Put cursor to the end after changing contacts
+    public void putCursor() {
+        statusList.get(statusList.size() - 1).setCursorToEnd = true;
+        notifyItemChanged(statusList.size() - 1);
+    }
+
+    /**
+     * Display attachment for a holder
+     *
+     * @param holder                - view related to a compose element {@link ComposeViewHolder}
+     * @param position              - int position of the message in the thread
+     * @param scrollToMediaPosition - int the position to scroll to media
+     */
+    private void displayAttachments(ComposeViewHolder holder, int position, int scrollToMediaPosition) {
+        if (statusList.size() > position && statusList.get(position).media_attachments != null) {
+            holder.binding.attachmentsList.removeAllViews();
+            List<Attachment> attachmentList = statusList.get(position).media_attachments;
+            if (attachmentList != null && attachmentList.size() > 0) {
+                holder.binding.sensitiveMedia.setVisibility(View.VISIBLE);
+                if (!statusList.get(position).sensitive) {
+                    if (currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) {
+                        holder.binding.sensitiveMedia.setChecked(currentAccount.mastodon_account.source.sensitive);
+                        statusList.get(position).sensitive = currentAccount.mastodon_account.source.sensitive;
+                    } else {
+                        statusList.get(position).sensitive = false;
+                    }
+                }
+
+                holder.binding.sensitiveMedia.setOnCheckedChangeListener((buttonView, isChecked) -> statusList.get(position).sensitive = isChecked);
+                int mediaPosition = 0;
+                for (Attachment attachment : attachmentList) {
+                    ComposeAttachmentItemBinding composeAttachmentItemBinding = ComposeAttachmentItemBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false);
+                    composeAttachmentItemBinding.buttonPlay.setVisibility(View.GONE);
+                    String attachmentPath = attachment.local_path != null && !attachment.local_path.trim().isEmpty() ? attachment.local_path : attachment.preview_url;
+                    if (attachment.type != null || attachment.mimeType != null) {
+                        if ((attachment.type != null && attachment.type.toLowerCase().startsWith("image")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("image"))) {
+                            Glide.with(composeAttachmentItemBinding.preview.getContext())
+                                    .load(attachmentPath)
+                                    .diskCacheStrategy(DiskCacheStrategy.NONE)
+                                    .skipMemoryCache(true)
+                                    .into(composeAttachmentItemBinding.preview);
+                        } else if ((attachment.type != null && attachment.type.toLowerCase().startsWith("video")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("video"))) {
+                            composeAttachmentItemBinding.buttonPlay.setVisibility(View.VISIBLE);
+                            long interval = 2000;
+                            RequestOptions options = new RequestOptions().frame(interval);
+                            Glide.with(composeAttachmentItemBinding.preview.getContext()).asBitmap()
+                                    .load(attachmentPath)
+                                    .apply(options)
+                                    .into(composeAttachmentItemBinding.preview);
+                        } else if ((attachment.type != null && attachment.type.toLowerCase().startsWith("audio")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("audio"))) {
+                            Glide.with(composeAttachmentItemBinding.preview.getContext())
+                                    .load(R.drawable.ic_baseline_audio_file_24)
+                                    .into(composeAttachmentItemBinding.preview);
+                        } else {
+                            Glide.with(composeAttachmentItemBinding.preview.getContext())
+                                    .load(R.drawable.ic_baseline_insert_drive_file_24)
+                                    .into(composeAttachmentItemBinding.preview);
+                        }
+                    } else {
+                        Glide.with(composeAttachmentItemBinding.preview.getContext())
+                                .load(R.drawable.ic_baseline_insert_drive_file_24)
+                                .into(composeAttachmentItemBinding.preview);
+                    }
+                    if (mediaPosition == 0) {
+                        composeAttachmentItemBinding.buttonOrderUp.setVisibility(View.INVISIBLE);
+                    } else {
+                        composeAttachmentItemBinding.buttonOrderUp.setVisibility(View.VISIBLE);
+                    }
+                    if (mediaPosition == attachmentList.size() - 1) {
+                        composeAttachmentItemBinding.buttonOrderDown.setVisibility(View.INVISIBLE);
+                    } else {
+                        composeAttachmentItemBinding.buttonOrderDown.setVisibility(View.VISIBLE);
+                    }
+                    //Remote attachments when deleting/redrafting can't be ordered
+                    if (attachment.local_path == null) {
+                        composeAttachmentItemBinding.buttonOrderUp.setVisibility(View.INVISIBLE);
+                        composeAttachmentItemBinding.buttonOrderDown.setVisibility(View.INVISIBLE);
+                    }
+                    int finalMediaPosition = mediaPosition;
+                    composeAttachmentItemBinding.editPreview.setOnClickListener(v -> {
+                        Intent intent = new Intent(context, EditImageActivity.class);
+                        Bundle b = new Bundle();
+                        intent.putExtra("imageUri", attachment.local_path);
+                        intent.putExtras(b);
+                        context.startActivity(intent);
+                    });
+                    composeAttachmentItemBinding.buttonDescription.setOnClickListener(v -> {
+                        AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle());
+                        builderInner.setTitle(R.string.upload_form_description);
+                        PopupMediaDescriptionBinding popupMediaDescriptionBinding = PopupMediaDescriptionBinding.inflate(LayoutInflater.from(context), null, false);
+                        builderInner.setView(popupMediaDescriptionBinding.getRoot());
+
+                        popupMediaDescriptionBinding.mediaDescription.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1500)});
+                        popupMediaDescriptionBinding.mediaDescription.requestFocus();
+                        Glide.with(popupMediaDescriptionBinding.mediaPicture.getContext())
+                                .asBitmap()
+                                .load(attachmentPath)
+                                .into(new CustomTarget<Bitmap>() {
+                                    @Override
+                                    public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
+                                        popupMediaDescriptionBinding.mediaPicture.setImageBitmap(resource);
+                                    }
+
+                                    @Override
+                                    public void onLoadCleared(@Nullable Drawable placeholder) {
+
+                                    }
+                                });
+                        builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+                        if (attachment.description != null) {
+                            popupMediaDescriptionBinding.mediaDescription.setText(attachment.description);
+                            popupMediaDescriptionBinding.mediaDescription.setSelection(popupMediaDescriptionBinding.mediaDescription.getText().length());
+                        }
+                        builderInner.setPositiveButton(R.string.validate, (dialog, which) -> {
+                            attachment.description = popupMediaDescriptionBinding.mediaDescription.getText().toString();
+                            displayAttachments(holder, position, finalMediaPosition);
+                            dialog.dismiss();
+                        });
+                        AlertDialog alertDialog = builderInner.create();
+                        Objects.requireNonNull(alertDialog.getWindow()).setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+                        alertDialog.show();
+                        popupMediaDescriptionBinding.mediaDescription.requestFocus();
+
+                    });
+
+                    composeAttachmentItemBinding.buttonOrderUp.setOnClickListener(v -> {
+                        if (finalMediaPosition > 0 && attachmentList.size() > 1) {
+                            Attachment at1 = attachmentList.get(finalMediaPosition);
+                            Attachment at2 = attachmentList.get(finalMediaPosition - 1);
+                            attachmentList.set(finalMediaPosition - 1, at1);
+                            attachmentList.set(finalMediaPosition, at2);
+                            displayAttachments(holder, position, finalMediaPosition - 1);
+                        }
+                    });
+                    composeAttachmentItemBinding.buttonOrderDown.setOnClickListener(v -> {
+                        if (finalMediaPosition < (attachmentList.size() - 1) && attachmentList.size() > 1) {
+                            Attachment at1 = attachmentList.get(finalMediaPosition);
+                            Attachment at2 = attachmentList.get(finalMediaPosition + 1);
+                            attachmentList.set(finalMediaPosition, at2);
+                            attachmentList.set(finalMediaPosition + 1, at1);
+                            displayAttachments(holder, position, finalMediaPosition + 1);
+                        }
+                    });
+                    composeAttachmentItemBinding.buttonRemove.setOnClickListener(v -> {
+                        AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle());
+                        builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+                        builderInner.setPositiveButton(R.string.delete, (dialog, which) -> {
+                            attachmentList.remove(attachment);
+                            displayAttachments(holder, position, finalMediaPosition);
+                            new Thread(() -> {
+                                if (attachment.local_path != null) {
+                                    File fileToDelete = new File(attachment.local_path);
+                                    if (fileToDelete.exists()) {
+                                        //noinspection ResultOfMethodCallIgnored
+                                        fileToDelete.delete();
+                                    }
+                                }
+                            }).start();
+
+                        });
+                        builderInner.setMessage(R.string.toot_delete_media);
+                        builderInner.show();
+                    });
+                    composeAttachmentItemBinding.preview.setOnClickListener(v -> displayAttachments(holder, position, finalMediaPosition));
+                    if (attachment.description == null || attachment.description.trim().isEmpty()) {
+                        composeAttachmentItemBinding.buttonDescription.setIconResource(R.drawable.ic_baseline_warning_24);
+                        composeAttachmentItemBinding.buttonDescription.setStrokeColor(ThemeHelper.getNoDescriptionColorStateList(context));
+                        composeAttachmentItemBinding.buttonDescription.setTextColor(ContextCompat.getColor(context, R.color.no_description));
+                        Helper.changeDrawableColor(context, R.drawable.ic_baseline_warning_24, ContextCompat.getColor(context, R.color.no_description));
+                        composeAttachmentItemBinding.buttonDescription.setIconTint(ThemeHelper.getNoDescriptionColorStateList(context));
+                    } else {
+                        composeAttachmentItemBinding.buttonDescription.setIconTint(ThemeHelper.getHavingDescriptionColorStateList(context));
+                        composeAttachmentItemBinding.buttonDescription.setIconResource(R.drawable.ic_baseline_check_circle_24);
+                        composeAttachmentItemBinding.buttonDescription.setTextColor(ContextCompat.getColor(context, R.color.having_description));
+                        composeAttachmentItemBinding.buttonDescription.setStrokeColor(ThemeHelper.getHavingDescriptionColorStateList(context));
+                        Helper.changeDrawableColor(context, R.drawable.ic_baseline_check_circle_24, ContextCompat.getColor(context, R.color.having_description));
+                    }
+                    holder.binding.attachmentsList.addView(composeAttachmentItemBinding.getRoot());
+                    mediaPosition++;
+                }
+                holder.binding.attachmentsList.setVisibility(View.VISIBLE);
+                if (scrollToMediaPosition >= 0 && holder.binding.attachmentsList.getChildCount() < scrollToMediaPosition) {
+                    holder.binding.attachmentsList.requestChildFocus(holder.binding.attachmentsList.getChildAt(scrollToMediaPosition), holder.binding.attachmentsList.getChildAt(scrollToMediaPosition));
+                }
+            } else {
+                holder.binding.attachmentsList.setVisibility(View.GONE);
+                holder.binding.sensitiveMedia.setVisibility(View.GONE);
+            }
+        } else {
+            holder.binding.attachmentsList.setVisibility(View.GONE);
+            holder.binding.sensitiveMedia.setVisibility(View.GONE);
+        }
+        buttonState(holder);
+    }
+
+    /**
+     * Manage state of media and poll button
+     *
+     * @param holder ComposeViewHolder
+     */
+    private void buttonState(ComposeViewHolder holder) {
+        if (BaseMainActivity.software == null || BaseMainActivity.software.toUpperCase().compareTo("MASTODON") == 0) {
+            if (holder.getBindingAdapterPosition() > 0) {
+                Status statusDraft = statusList.get(holder.getBindingAdapterPosition());
+                if (statusDraft.poll == null) {
+                    holder.binding.buttonAttachImage.setEnabled(true);
+                    holder.binding.buttonAttachVideo.setEnabled(true);
+                    holder.binding.buttonAttachAudio.setEnabled(true);
+                    holder.binding.buttonAttachManual.setEnabled(true);
+                } else {
+                    holder.binding.buttonAttachImage.setEnabled(false);
+                    holder.binding.buttonAttachVideo.setEnabled(false);
+                    holder.binding.buttonAttachAudio.setEnabled(false);
+                    holder.binding.buttonAttachManual.setEnabled(false);
+                    holder.binding.buttonPoll.setEnabled(true);
+                }
+                holder.binding.buttonPoll.setEnabled(statusDraft.media_attachments == null || statusDraft.media_attachments.size() <= 0);
+            }
+        }
+    }
+
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public int getItemCount() {
+        return statusList.size();
+    }
+
     @SuppressLint("ClickableViewAccessibility")
     @Override
     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
@@ -1339,6 +1346,9 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
 
                 @Override
                 public void afterTextChanged(Editable s) {
+                    if (promptDraftListener != null) {
+                        promptDraftListener.promptDraft();
+                    }
                     int currentLength = MastodonHelper.countLength(holder);
                     int max_car = MastodonHelper.getInstanceMaxChars(context);
                     if (currentLength > max_car) {
@@ -1440,40 +1450,6 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
 
     }
 
-
-    /**
-     * Display the emoji picker in the current message
-     *
-     * @param holder - view for the message {@link ComposeViewHolder}
-     * @throws DBException
-     */
-    private void displayEmojiPicker(ComposeViewHolder holder) throws DBException {
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
-        int paddingPixel = 15;
-        float density = context.getResources().getDisplayMetrics().density;
-        int paddingDp = (int) (paddingPixel * density);
-        builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
-        builder.setTitle(R.string.insert_emoji);
-        if (emojis != null && emojis.size() > 0) {
-            GridView gridView = new GridView(context);
-            gridView.setAdapter(new EmojiAdapter(emojis.get(BaseMainActivity.currentInstance)));
-            gridView.setNumColumns(5);
-            gridView.setOnItemClickListener((parent, view, position, id) -> {
-                holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + emojis.get(BaseMainActivity.currentInstance).get(position).shortcode + ": ");
-                alertDialogEmoji.dismiss();
-            });
-            gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
-            builder.setView(gridView);
-        } else {
-            TextView textView = new TextView(context);
-            textView.setText(context.getString(R.string.no_emoji));
-            textView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
-            builder.setView(textView);
-        }
-        alertDialogEmoji = builder.show();
-    }
-
     /**
      * Display the popup to attach a poll to message
      *
@@ -1655,7 +1631,9 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
                             expire = 864000;
                     }
                     statusDraft.poll.expire_in = expire;
-
+                    if (promptDraftListener != null) {
+                        promptDraftListener.promptDraft();
+                    }
                     List<Poll.PollItem> pollItems = new ArrayList<>();
                     int childCount = composePollBinding.optionsList.getChildCount();
                     for (int i = 0; i < childCount; i++) {
@@ -1689,6 +1667,44 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
         alertPollDiaslog.show();
     }
 
+
+    /**
+     * Display the emoji picker in the current message
+     *
+     * @param holder - view for the message {@link ComposeViewHolder}
+     * @throws DBException
+     */
+    private void displayEmojiPicker(ComposeViewHolder holder) throws DBException {
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
+        int paddingPixel = 15;
+        float density = context.getResources().getDisplayMetrics().density;
+        int paddingDp = (int) (paddingPixel * density);
+        builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+        builder.setTitle(R.string.insert_emoji);
+        if (emojis != null && emojis.size() > 0) {
+            GridView gridView = new GridView(context);
+            gridView.setAdapter(new EmojiAdapter(emojis.get(BaseMainActivity.currentInstance)));
+            gridView.setNumColumns(5);
+            gridView.setOnItemClickListener((parent, view, position, id) -> {
+                holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + emojis.get(BaseMainActivity.currentInstance).get(position).shortcode + ": ");
+                alertDialogEmoji.dismiss();
+            });
+            gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
+            builder.setView(gridView);
+        } else {
+            TextView textView = new TextView(context);
+            textView.setText(context.getString(R.string.no_emoji));
+            textView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
+            builder.setView(textView);
+        }
+        alertDialogEmoji = builder.show();
+    }
+
+    public interface promptDraftListener {
+        void promptDraft();
+    }
+
     public interface ManageDrafts {
         void onItemDraftAdded(int position);