From 7d9d2346ccb0fa3d29ff1aca6bace9fb3c857cd3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 21 Feb 2025 11:32:34 +0100 Subject: [PATCH] Allow to edit scheduled messages from server side --- .../mastodon/activities/ComposeActivity.java | 27 +++- .../android/mastodon/helper/Helper.java | 1 + .../android/mastodon/jobs/ComposeWorker.java | 26 ++- .../services/ThreadMessageService.java | 4 +- .../ui/drawer/StatusScheduledAdapter.java | 18 ++- .../FragmentMastodonDirectMessage.java | 2 +- .../fragment/timeline/FragmentScheduled.java | 150 ++++++++++-------- 7 files changed, 142 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java index 20f562c9..9dff1abb 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java @@ -17,6 +17,7 @@ package app.fedilab.android.mastodon.activities; import static app.fedilab.android.BaseMainActivity.currentInstance; import static app.fedilab.android.BaseMainActivity.emojis; +import static app.fedilab.android.mastodon.helper.Helper.TAG; import android.Manifest; import android.annotation.SuppressLint; @@ -34,6 +35,7 @@ import android.os.Looper; import android.text.Editable; import android.text.InputFilter; import android.text.TextWatcher; +import android.util.Log; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; @@ -129,7 +131,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana if (imgpath != null) { int position = 0; for (Status status : statusList) { - if (status.media_attachments != null && status.media_attachments.size() > 0) { + if (status.media_attachments != null && !status.media_attachments.isEmpty()) { for (Attachment attachment : status.media_attachments) { if (attachment.local_path != null && attachment.local_path.equalsIgnoreCase(imgpath)) { if (focusX != -2) { @@ -552,7 +554,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana status.in_reply_to_id = scheduledStatus.params.in_reply_to_id; status.poll = scheduledStatus.params.poll; - if (scheduledStatus.params.media_ids != null && scheduledStatus.params.media_ids.size() > 0) { + if (scheduledStatus.params.media_ids != null && !scheduledStatus.params.media_ids.isEmpty()) { status.media_attachments = new ArrayList<>(); new Thread(() -> { StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class); @@ -565,6 +567,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana status.sensitive = scheduledStatus.params.sensitive; status.spoiler_text = scheduledStatus.params.spoiler_text; status.visibility = scheduledStatus.params.visibility; + statuses.add(status); statusDraft.statusDraftList = statuses; } if (account == null) { @@ -753,7 +756,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana }, 0, 10000); } - if (sharedAttachments != null && sharedAttachments.size() > 0) { + if (sharedAttachments != null && !sharedAttachments.isEmpty()) { for (Attachment attachment : sharedAttachments) { composeAdapter.addAttachment(-1, attachment); } @@ -844,7 +847,12 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana private void storeDraft(boolean sendMessage) { - storeDraft(sendMessage, null); + String scheduledDate = null; + if(scheduledStatus != null && scheduledStatus.scheduled_at != null) { + SimpleDateFormat sdf = new SimpleDateFormat(Helper.SCHEDULE_DATE_FORMAT, Locale.getDefault()); + scheduledDate = sdf.format(scheduledStatus.scheduled_at.getTime()); + } + storeDraft(sendMessage, scheduledDate); } private void storeDraft(boolean sendMessage, String scheduledDate) { @@ -869,11 +877,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana WorkManager.getInstance(ComposeActivity.this).cancelWorkById(statusDraft.workerUuid); } } - if (statusReplies.size() > 0) { + if (!statusReplies.isEmpty()) { statusDraft.statusReplyList = new ArrayList<>(); statusDraft.statusReplyList.addAll(statusReplies); } - if (statusDrafts.size() > 0) { + if (!statusDrafts.isEmpty()) { statusDraft.statusDraftList = new ArrayList<>(); statusDraft.statusDraftList.addAll(statusDrafts); } @@ -906,7 +914,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } else { Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show(); } - if (statusDrafts.size() > 0) { + if (!statusDrafts.isEmpty()) { statusDrafts.get(statusDrafts.size() - 1).submitted = false; composeAdapter.notifyItemChanged(statusList.size() - 1); } @@ -979,12 +987,14 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana mediaCount += status.media_attachments != null ? status.media_attachments.size() : 0; } if (mediaCount > 0) { + String scheduledStatusId = scheduledStatus!=null&&scheduledStatus.id!=null?scheduledStatus.id:null; Data inputData = new Data.Builder() .putString(Helper.ARG_STATUS_DRAFT_ID, String.valueOf(statusDraft.id)) .putString(Helper.ARG_INSTANCE, instance) .putString(Helper.ARG_TOKEN, token) .putString(Helper.ARG_EDIT_STATUS_ID, editMessageId) .putString(Helper.ARG_USER_ID, account.user_id) + .putString(Helper.ARG_SCHEDULED_ID, scheduledStatusId) .putString(Helper.ARG_SCHEDULED_DATE, scheduledDate).build(); OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ComposeWorker.class) .setInputData(inputData) @@ -993,7 +1003,8 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana WorkManager.getInstance(ComposeActivity.this).enqueue(request); } else { - new ThreadMessageService(ComposeActivity.this, instance, account.user_id, token, statusDraft, scheduledDate, editMessageId); + String scheduledStatusId = scheduledStatus!=null&&scheduledStatus.id!=null?scheduledStatus.id:null; + new ThreadMessageService(ComposeActivity.this, instance, account.user_id, token, statusDraft, scheduledDate, editMessageId, scheduledStatusId); } finish(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java index 025f5f8e..e52b7991 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java @@ -294,6 +294,7 @@ public class Helper { public static final String ARG_MEDIA_ARRAY_PROFILE = "ARG_MEDIA_ARRAY_PROFILE"; public static final String ARG_VISIBILITY = "ARG_VISIBILITY"; public static final String ARG_SCHEDULED_DATE = "ARG_SCHEDULED_DATE"; + public static final String ARG_SCHEDULED_ID = "ARG_SCHEDULED_ID"; public static final String WORKER_REFRESH_NOTIFICATION = "WORKER_REFRESH_NOTIFICATION"; public static final String WORKER_REFRESH_HOME = "WORKER_REFRESH_HOME"; diff --git a/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java b/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java index e8ddb9d4..e128c415 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java +++ b/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java @@ -16,6 +16,7 @@ package app.fedilab.android.mastodon.jobs; import static android.content.Context.NOTIFICATION_SERVICE; + import android.app.IntentService; import android.app.Notification; import android.app.NotificationChannel; @@ -114,7 +115,7 @@ public class ComposeWorker extends Worker { MastodonStatusesService mastodonStatusesService = init(context, dataPost.instance); boolean error = false; Status firstSendMessage = null; - if (dataPost.statusDraft != null && dataPost.statusDraft.statusDraftList != null && dataPost.statusDraft.statusDraftList.size() > 0) { + if (dataPost.statusDraft != null && dataPost.statusDraft.statusDraftList != null && !dataPost.statusDraft.statusDraftList.isEmpty()) { //If state is null, it is created (typically when submitting the status the first time) if (dataPost.statusDraft.state == null) { dataPost.statusDraft.state = new PostState(); @@ -137,7 +138,7 @@ public class ComposeWorker extends Worker { List statuses = dataPost.statusDraft.statusDraftList; String in_reply_to_status = null; - if (dataPost.statusDraft.statusReplyList != null && dataPost.statusDraft.statusReplyList.size() > 0) { + if (dataPost.statusDraft.statusReplyList != null && !dataPost.statusDraft.statusReplyList.isEmpty()) { in_reply_to_status = dataPost.statusDraft.statusReplyList.get(dataPost.statusDraft.statusReplyList.size() - 1).id; } totalMediaSize = 0; @@ -146,13 +147,13 @@ public class ComposeWorker extends Worker { boolean watermark = sharedPreferences.getBoolean(context.getString(R.string.SET_WATERMARK), false); String watermarkText = sharedPreferences.getString(context.getString(R.string.SET_WATERMARK_TEXT) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, null); for (int i = startingPosition; i < statuses.size(); i++) { - if (statuses.get(i).media_attachments != null && statuses.get(i).media_attachments.size() > 0) { + if (statuses.get(i).media_attachments != null && !statuses.get(i).media_attachments.isEmpty()) { for (Attachment attachment : statuses.get(i).media_attachments) { totalMediaSize += attachment.size; } } } - if (watermarkText == null || watermarkText.trim().length() == 0) { + if (watermarkText == null || watermarkText.trim().isEmpty()) { try { BaseAccount account = new Account(context).getAccountByToken(dataPost.token); watermarkText = account.mastodon_account.username + "@" + account.instance; @@ -174,7 +175,7 @@ public class ComposeWorker extends Worker { } //post media first List attachmentIds = null; - if (statuses.get(i).media_attachments != null && statuses.get(i).media_attachments.size() > 0) { + if (statuses.get(i).media_attachments != null && !statuses.get(i).media_attachments.isEmpty()) { attachmentIds = new ArrayList<>(); for (Attachment attachment : statuses.get(i).media_attachments) { if (attachment.id != null) { @@ -248,7 +249,7 @@ public class ComposeWorker extends Worker { statuses.get(i).text += " \uD83D\uDC41"; } //Record tags - if (statuses.get(i).text != null && statuses.get(i).text.length() > 0) { + if (statuses.get(i).text != null && !statuses.get(i).text.isEmpty()) { Matcher matcher = Helper.hashtagPattern.matcher(statuses.get(i).text); while (matcher.find()) { int matchStart = matcher.start(1); @@ -257,7 +258,7 @@ public class ComposeWorker extends Worker { if (matchStart >= 0 && matchEnd < statuses.get(i).text.length()) { String tag = statuses.get(i).text.substring(matchStart, matchEnd); tag = tag.replace("#", ""); - if (tag.length() > 0) { + if (!tag.isEmpty()) { try { new CamelTag(context).insert(tag); } catch (DBException e) { @@ -363,6 +364,14 @@ public class ComposeWorker extends Worker { return; } } else { + Call voidCall = mastodonStatusesService.deleteScheduledStatus(dataPost.token, dataPost.scheduledId); + if (voidCall != null) { + try { + voidCall.execute(); + } catch (Exception e) { + e.printStackTrace(); + } + } Call scheduledStatusCall = mastodonStatusesService.createScheduledStatus(null, dataPost.token, statuses.get(i).text, attachmentIds, poll_options, poll_expire_in, poll_multiple, poll_hide_totals, statuses.get(i).quote_id == null ? in_reply_to_status : null, statuses.get(i).sensitive, statuses.get(i).spoilerChecked ? statuses.get(i).spoiler_text : null, statuses.get(i).visibility.toLowerCase(), dataPost.scheduledDate, statuses.get(i).language); try { @@ -489,6 +498,7 @@ public class ComposeWorker extends Worker { } } String token = inputData.getString(Helper.ARG_TOKEN); + String scheduledId = inputData.getString(Helper.ARG_SCHEDULED_ID); String instance = inputData.getString(Helper.ARG_INSTANCE); String userId = inputData.getString(Helper.ARG_USER_ID); String scheduledDate = inputData.getString(Helper.ARG_SCHEDULED_DATE); @@ -503,6 +513,7 @@ public class ComposeWorker extends Worker { DataPost dataPost = new DataPost(); dataPost.instance = instance; dataPost.token = token; + dataPost.scheduledId = scheduledId; dataPost.userId = userId; dataPost.statusDraft = statusDraft; dataPost.scheduledDate = scheduledDate; @@ -582,6 +593,7 @@ public class ComposeWorker extends Worker { public String instance; public String token; public String userId; + public String scheduledId; public String statusEditId; public StatusDraft statusDraft; public int messageToSend; diff --git a/app/src/main/java/app/fedilab/android/mastodon/services/ThreadMessageService.java b/app/src/main/java/app/fedilab/android/mastodon/services/ThreadMessageService.java index 263eda13..2de9c122 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/services/ThreadMessageService.java +++ b/app/src/main/java/app/fedilab/android/mastodon/services/ThreadMessageService.java @@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.services; * see . */ + import android.content.Context; import app.fedilab.android.mastodon.client.entities.app.StatusDraft; @@ -22,7 +23,7 @@ import app.fedilab.android.mastodon.jobs.ComposeWorker; public class ThreadMessageService { - public ThreadMessageService(Context context, String instance, String userId, String token, StatusDraft statusDraft, String scheduledDate, String editMessageId) { + public ThreadMessageService(Context context, String instance, String userId, String token, StatusDraft statusDraft, String scheduledDate, String editMessageId, String scheduledStatusId) { ComposeWorker.DataPost dataPost = new ComposeWorker.DataPost(); dataPost.instance = instance; dataPost.userId = userId; @@ -30,6 +31,7 @@ public class ThreadMessageService { dataPost.scheduledDate = scheduledDate; dataPost.statusDraft = statusDraft; dataPost.statusEditId = editMessageId; + dataPost.scheduledId = scheduledStatusId; ComposeWorker.publishMessage(context, dataPost); } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusScheduledAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusScheduledAdapter.java index 3b6743f8..8e772da0 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusScheduledAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusScheduledAdapter.java @@ -17,12 +17,15 @@ package app.fedilab.android.mastodon.ui.drawer; import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY; +import static app.fedilab.android.mastodon.helper.Helper.TAG; + import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.text.Html; import android.text.SpannableString; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -45,6 +48,7 @@ import app.fedilab.android.R; import app.fedilab.android.databinding.DrawerStatusScheduledBinding; import app.fedilab.android.mastodon.activities.ComposeActivity; import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus; +import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.app.CachedBundle; import app.fedilab.android.mastodon.client.entities.app.ScheduledBoost; import app.fedilab.android.mastodon.client.entities.app.StatusDraft; @@ -126,10 +130,14 @@ public class StatusScheduledAdapter extends RecyclerView.Adapter { - if (statusDraft != null) { + if (statusDraftList != null || scheduledStatuses != null) { Intent intent = new Intent(context, ComposeActivity.class); Bundle args = new Bundle(); - args.putSerializable(Helper.ARG_STATUS_DRAFT, statusDraft); + if(statusDraftList != null) { + args.putSerializable(Helper.ARG_STATUS_DRAFT, statusDraftList.get(position)); + } else { + args.putSerializable(Helper.ARG_STATUS_SCHEDULED, scheduledStatuses.get(position)); + } new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { Bundle bundle = new Bundle(); bundle.putLong(Helper.ARG_INTENT_ID, bundleId); @@ -149,7 +157,7 @@ public class StatusScheduledAdapter extends RecyclerView.Adapter { if (scheduledStatuses != null) { scheduledStatuses.remove(scheduledStatus); - if (scheduledStatuses.size() == 0) { + if (scheduledStatuses.isEmpty()) { scheduledActions.onAllDeleted(); } notifyItemRemoved(position); @@ -161,7 +169,7 @@ public class StatusScheduledAdapter extends RecyclerView.Adapter { + binding.loader.setVisibility(View.GONE); + if (scheduledStatuses != null && scheduledStatuses.scheduledStatuses != null && !scheduledStatuses.scheduledStatuses.isEmpty()) { + StatusScheduledAdapter statusScheduledAdapter = new StatusScheduledAdapter(scheduledStatuses.scheduledStatuses, null, null); + statusScheduledAdapter.scheduledActions = FragmentScheduled.this; + binding.recyclerView.setAdapter(statusScheduledAdapter); + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireActivity()); + binding.recyclerView.setLayoutManager(linearLayoutManager); + + } else { + binding.noAction.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + } + }); + } + + private void displayScheduledDevice() { + new Thread(() -> { + try { + List scheduledDrafts = new StatusDraft(requireActivity()).geStatusDraftScheduledList(Helper.getCurrentAccount(requireActivity())); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + binding.loader.setVisibility(View.GONE); + if (scheduledDrafts != null && !scheduledDrafts.isEmpty()) { + StatusScheduledAdapter statusScheduledAdapter = new StatusScheduledAdapter(null, scheduledDrafts, null); + statusScheduledAdapter.scheduledActions = FragmentScheduled.this; + binding.recyclerView.setAdapter(statusScheduledAdapter); + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireActivity()); + binding.recyclerView.setLayoutManager(linearLayoutManager); + + } else { + binding.noAction.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + } + }; + mainHandler.post(myRunnable); + } catch (DBException e) { + e.printStackTrace(); + } + }).start(); + } + + private void displayScheduledBoost(){ + new Thread(() -> { + try { + List scheduledBoosts = new ScheduledBoost(requireActivity()).getScheduled(Helper.getCurrentAccount(requireActivity())); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + binding.loader.setVisibility(View.GONE); + if (scheduledBoosts != null && !scheduledBoosts.isEmpty()) { + StatusScheduledAdapter statusScheduledAdapter = new StatusScheduledAdapter(null, null, scheduledBoosts); + statusScheduledAdapter.scheduledActions = FragmentScheduled.this; + binding.recyclerView.setAdapter(statusScheduledAdapter); + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireActivity()); + binding.recyclerView.setLayoutManager(linearLayoutManager); + + } else { + binding.noAction.setVisibility(View.VISIBLE); + binding.noActionText.setText(R.string.no_scheduled_boosts); + binding.recyclerView.setVisibility(View.GONE); + } + }; + mainHandler.post(myRunnable); + } catch (DBException e) { + e.printStackTrace(); + } + }).start(); + } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); binding.loader.setVisibility(View.VISIBLE); if (type == Timeline.TimeLineEnum.SCHEDULED_TOOT_SERVER) { - StatusesVM statusesVM = new ViewModelProvider(requireActivity()).get(StatusesVM.class); - statusesVM.getScheduledStatuses(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(requireActivity(), scheduledStatuses -> { - binding.loader.setVisibility(View.GONE); - if (scheduledStatuses != null && scheduledStatuses.scheduledStatuses != null && scheduledStatuses.scheduledStatuses.size() > 0) { - StatusScheduledAdapter statusScheduledAdapter = new StatusScheduledAdapter(scheduledStatuses.scheduledStatuses, null, null); - statusScheduledAdapter.scheduledActions = FragmentScheduled.this; - binding.recyclerView.setAdapter(statusScheduledAdapter); - LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireActivity()); - binding.recyclerView.setLayoutManager(linearLayoutManager); - - } else { - binding.noAction.setVisibility(View.VISIBLE); - binding.recyclerView.setVisibility(View.GONE); - } - }); + displayScheduledServer(); } else if (type == Timeline.TimeLineEnum.SCHEDULED_TOOT_CLIENT) { - new Thread(() -> { - try { - List scheduledDrafts = new StatusDraft(requireActivity()).geStatusDraftScheduledList(Helper.getCurrentAccount(requireActivity())); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - binding.loader.setVisibility(View.GONE); - if (scheduledDrafts != null && scheduledDrafts.size() > 0) { - StatusScheduledAdapter statusScheduledAdapter = new StatusScheduledAdapter(null, scheduledDrafts, null); - statusScheduledAdapter.scheduledActions = FragmentScheduled.this; - binding.recyclerView.setAdapter(statusScheduledAdapter); - LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireActivity()); - binding.recyclerView.setLayoutManager(linearLayoutManager); - - } else { - binding.noAction.setVisibility(View.VISIBLE); - binding.recyclerView.setVisibility(View.GONE); - } - }; - mainHandler.post(myRunnable); - } catch (DBException e) { - e.printStackTrace(); - } - }).start(); - + displayScheduledDevice(); } else if (type == Timeline.TimeLineEnum.SCHEDULED_BOOST) { - new Thread(() -> { - try { - List scheduledBoosts = new ScheduledBoost(requireActivity()).getScheduled(Helper.getCurrentAccount(requireActivity())); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - binding.loader.setVisibility(View.GONE); - if (scheduledBoosts != null && scheduledBoosts.size() > 0) { - StatusScheduledAdapter statusScheduledAdapter = new StatusScheduledAdapter(null, null, scheduledBoosts); - statusScheduledAdapter.scheduledActions = FragmentScheduled.this; - binding.recyclerView.setAdapter(statusScheduledAdapter); - LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireActivity()); - binding.recyclerView.setLayoutManager(linearLayoutManager); - - } else { - binding.noAction.setVisibility(View.VISIBLE); - binding.noActionText.setText(R.string.no_scheduled_boosts); - binding.recyclerView.setVisibility(View.GONE); - } - }; - mainHandler.post(myRunnable); - } catch (DBException e) { - e.printStackTrace(); - } - }).start(); + displayScheduledBoost(); } }