From e8c79cce65bdb4f6de66509e61d4bece4a73bc6f Mon Sep 17 00:00:00 2001 From: remi6397 Date: Sun, 6 May 2018 11:07:10 +0200 Subject: [PATCH] Notification tweaks: Grouping and Quick Reply button (#587) * Added notification grouping and Quick Reply button * Legal stuff * Coding style * Check whether account still exists when sending a quick reply * Add "compose" button * Polish translation * Improve strings * Code style * Cancel notification when user hits "compose" button * Notification counter * Make sure to open ComposeActivity for notification recipient account * Add ability to request account switch when starting an activity --- app/src/main/AndroidManifest.xml | 5 + .../keylesspalace/tusky/AccountActivity.java | 2 - .../com/keylesspalace/tusky/BaseActivity.java | 10 + .../keylesspalace/tusky/ComposeActivity.java | 2 - .../com/keylesspalace/tusky/MainActivity.java | 2 - .../tusky/di/ActivitiesModule.kt | 4 + .../tusky/di/BroadcastReceiverModule.kt | 7 +- .../receiver/SendStatusBroadcastReceiver.kt | 145 +++++++++++ .../tusky/util/NotificationHelper.java | 239 ++++++++++++++---- app/src/main/res/values-pl/strings.xml | 5 + app/src/main/res/values/strings.xml | 9 +- 11 files changed, 369 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1b7e96dca..97e6a2da8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -101,6 +101,11 @@ + + dispatchingAndroidInjector; private String accountId; diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 0b691831d..f624a59cf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -34,8 +34,13 @@ import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.util.ThemeUtils; +import javax.inject.Inject; + public abstract class BaseActivity extends AppCompatActivity { + @Inject + public AccountManager accountManager; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -48,6 +53,11 @@ public abstract class BaseActivity extends AppCompatActivity { String theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT); ThemeUtils.setAppNightMode(theme, this); + long accountId = getIntent().getLongExtra("account", -1); + if (accountId != -1) { + accountManager.setActiveAccount(accountId); + } + int style; switch (preferences.getString("statusTextSize", "medium")) { case "large": diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 1bfe8ea52..52d383af2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -167,8 +167,6 @@ public final class ComposeActivity @Inject public MastodonApi mastodonApi; - @Inject - public AccountManager accountManager; private TextView replyTextView; private TextView replyContentTextView; diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index eee24f2d9..d41f23664 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -92,8 +92,6 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity, public MastodonApi mastodonApi; @Inject public DispatchingAndroidInjector fragmentInjector; - @Inject - public AccountManager accountManager; private static int COMPOSE_RESULT = 1; diff --git a/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt index e36ff9ebb..dde3df4f0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt @@ -25,6 +25,10 @@ import dagger.android.ContributesAndroidInjector @Module abstract class ActivitiesModule { + + @ContributesAndroidInjector + abstract fun contributesBaseActivity(): BaseActivity + @ContributesAndroidInjector(modules = [FragmentBuildersModule::class]) abstract fun contributesMainActivity(): MainActivity diff --git a/app/src/main/java/com/keylesspalace/tusky/di/BroadcastReceiverModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/BroadcastReceiverModule.kt index 335da5599..df4b09ff2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/BroadcastReceiverModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/BroadcastReceiverModule.kt @@ -1,4 +1,5 @@ -/* Copyright 2018 Conny Duck +/* Copyright 2018 Jeremiasz Nelz + * Copyright 2018 Conny Duck * * This file is a part of Tusky. * @@ -15,6 +16,7 @@ package com.keylesspalace.tusky.di +import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver import dagger.Module import dagger.android.ContributesAndroidInjector @@ -22,5 +24,8 @@ import dagger.android.ContributesAndroidInjector @Module abstract class BroadcastReceiverModule { @ContributesAndroidInjector + + abstract fun contributeSendStatusBroadcastReceiver() : SendStatusBroadcastReceiver + abstract fun contributeNotificationClearBroadcastReceiver() : NotificationClearBroadcastReceiver } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt new file mode 100644 index 000000000..8b2e535f9 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt @@ -0,0 +1,145 @@ +/* Copyright 2018 Jeremiasz Nelz + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.receiver + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.support.v4.app.NotificationCompat +import android.support.v4.app.NotificationManagerCompat +import android.support.v4.app.RemoteInput +import android.support.v4.content.ContextCompat +import android.util.Log +import com.keylesspalace.tusky.ComposeActivity +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.db.AccountManager +import com.keylesspalace.tusky.entity.Status +import com.keylesspalace.tusky.service.SendTootService +import com.keylesspalace.tusky.util.NotificationHelper +import dagger.android.AndroidInjection +import java.util.* +import javax.inject.Inject + +private const val TAG = "SendStatusBR" + +class SendStatusBroadcastReceiver : BroadcastReceiver() { + + @Inject + lateinit var accountManager: AccountManager + + override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + + val notificationId = intent.getIntExtra(NotificationHelper.KEY_NOTIFICATION_ID, -1) + val senderId = intent.getLongExtra(NotificationHelper.KEY_SENDER_ACCOUNT_ID, -1) + val senderIdentifier = intent.getStringExtra(NotificationHelper.KEY_SENDER_ACCOUNT_IDENTIFIER) + val senderFullName = intent.getStringExtra(NotificationHelper.KEY_SENDER_ACCOUNT_FULL_NAME) + val citedStatusId = intent.getStringExtra(NotificationHelper.KEY_CITED_STATUS_ID) + val visibility = intent.getSerializableExtra(NotificationHelper.KEY_VISIBILITY) as Status.Visibility + val spoiler = intent.getStringExtra(NotificationHelper.KEY_SPOILER) + val mentions = intent.getStringArrayExtra(NotificationHelper.KEY_MENTIONS) + val citedText = intent.getStringExtra(NotificationHelper.KEY_CITED_TEXT) + val localAuthorId = intent.getStringExtra(NotificationHelper.KEY_CITED_AUTHOR_LOCAL) + + val account = accountManager.getAccountById(senderId) + + val notificationManager = NotificationManagerCompat.from(context) + + + if (intent.action == NotificationHelper.REPLY_ACTION) { + + val message = getReplyMessage(intent) + + if (account == null) { + Log.w(TAG, "Account \"$senderId\" not found in database. Aborting quick reply!") + + val builder = NotificationCompat.Builder(context, NotificationHelper.CHANNEL_MENTION + senderIdentifier) + .setSmallIcon(R.drawable.ic_notify) + .setColor(ContextCompat.getColor(context, (R.color.primary))) + .setGroup(senderFullName) + .setDefaults(0) // So it doesn't ring twice, notify only in Target callback + + builder.setContentTitle(context.getString(R.string.error_generic)) + builder.setContentText(context.getString(R.string.error_sender_account_gone)) + + builder.setSubText(senderFullName) + builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + builder.setCategory(NotificationCompat.CATEGORY_SOCIAL) + builder.setOnlyAlertOnce(true) + + notificationManager.notify(notificationId, builder.build()) + } else { + val text = mentions.joinToString(" ", postfix = " ") { "@$it" } + message.toString() + + val sendIntent = SendTootService.sendTootIntent( + context, + text, + spoiler, + visibility, + false, + emptyList(), + emptyList(), + citedStatusId, + null, + null, + null, account, 0) + + context.startService(sendIntent) + + val builder = NotificationCompat.Builder(context, NotificationHelper.CHANNEL_MENTION + senderIdentifier) + .setSmallIcon(R.drawable.ic_notify) + .setColor(ContextCompat.getColor(context, (R.color.primary))) + .setGroup(senderFullName) + .setDefaults(0) // So it doesn't ring twice, notify only in Target callback + + builder.setContentTitle(context.getString(R.string.status_sent)) + builder.setContentText(context.getString(R.string.status_sent_long)) + + builder.setSubText(senderFullName) + builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + builder.setCategory(NotificationCompat.CATEGORY_SOCIAL) + builder.setOnlyAlertOnce(true) + + notificationManager.notify(notificationId, builder.build()) + } + } else if (intent.action == NotificationHelper.COMPOSE_ACTION) { + + context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) + + notificationManager.cancel(notificationId) + + accountManager.setActiveAccount(senderId) + + val composeIntent = ComposeActivity.IntentBuilder() + .inReplyToId(citedStatusId) + .replyVisibility(visibility) + .contentWarning(spoiler) + .mentionedUsernames(Arrays.asList(*mentions)) + .repyingStatusAuthor(localAuthorId) + .replyingStatusContent(citedText) + .build(context) + + context.startActivity(composeIntent) + } + } + + private fun getReplyMessage(intent: Intent): CharSequence { + val remoteInput = RemoteInput.getResultsFromIntent(intent) + + return remoteInput.getCharSequence(NotificationHelper.KEY_REPLY) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java index 858dc6e1b..bdbf1a713 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java @@ -1,4 +1,5 @@ -/* Copyright 2017 Andrew Dawson +/* Copyright 2018 Jeremiasz Nelz + * Copyright 2017 Andrew Dawson * * This file is a part of Tusky. * @@ -27,6 +28,8 @@ import android.os.Build; import android.provider.Settings; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; +import android.support.v4.app.RemoteInput; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.ContextCompat; import android.util.Log; @@ -34,10 +37,13 @@ import android.util.Log; import com.keylesspalace.tusky.MainActivity; import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.TuskyApplication; +import com.keylesspalace.tusky.ViewThreadActivity; import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.entity.Notification; +import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver; +import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver; import com.keylesspalace.tusky.view.RoundedTransformation; import com.squareup.picasso.Picasso; @@ -46,10 +52,14 @@ import org.json.JSONException; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; public class NotificationHelper { + private static int notificationId = 0; + /** * constants used in Intents */ @@ -57,13 +67,39 @@ public class NotificationHelper { private static final String TAG = "NotificationHelper"; + public static final String REPLY_ACTION = "REPLY_ACTION"; + + public static final String COMPOSE_ACTION = "COMPOSE_ACTION"; + + public static final String KEY_REPLY = "KEY_REPLY"; + + public static final String KEY_SENDER_ACCOUNT_ID = "KEY_SENDER_ACCOUNT_ID"; + + public static final String KEY_SENDER_ACCOUNT_IDENTIFIER = "KEY_SENDER_ACCOUNT_IDENTIFIER"; + + public static final String KEY_SENDER_ACCOUNT_FULL_NAME = "KEY_SENDER_ACCOUNT_FULL_NAME"; + + public static final String KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"; + + public static final String KEY_CITED_STATUS_ID = "KEY_CITED_STATUS_ID"; + + public static final String KEY_VISIBILITY = "KEY_VISIBILITY"; + + public static final String KEY_SPOILER = "KEY_SPOILER"; + + public static final String KEY_MENTIONS = "KEY_MENTIONS"; + + public static final String KEY_CITED_TEXT = "KEY_CITED_TEXT"; + + public static final String KEY_CITED_AUTHOR_LOCAL = "KEY_CITED_AUTHOR_LOCAL"; + /** * notification channels used on Android O+ **/ - private static final String CHANNEL_MENTION = "CHANNEL_MENTION"; - private static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW"; - private static final String CHANNEL_BOOST = "CHANNEL_BOOST"; - private static final String CHANNEL_FAVOURITE = " CHANNEL_FAVOURITE"; + public static final String CHANNEL_MENTION = "CHANNEL_MENTION"; + public static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW"; + public static final String CHANNEL_BOOST = "CHANNEL_BOOST"; + public static final String CHANNEL_FAVOURITE = "CHANNEL_FAVOURITE"; /** * Takes a given Mastodon notification and either creates a new Android notification or updates @@ -104,75 +140,172 @@ public class NotificationHelper { account.setActiveNotifications(currentNotifications.toString()); - //no need to save account, this will be done in the calling function + // Notification group member + // ========================= + final NotificationCompat.Builder builder = newNotification(context, body, account, false); - Intent resultIntent = new Intent(context, MainActivity.class); - resultIntent.putExtra(ACCOUNT_ID, account.getId()); - TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); - stackBuilder.addParentStack(MainActivity.class); - stackBuilder.addNextIntent(resultIntent); - PendingIntent resultPendingIntent = stackBuilder.getPendingIntent((int) account.getId(), - PendingIntent.FLAG_UPDATE_CURRENT); + notificationId++; - Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class); - deleteIntent.putExtra(ACCOUNT_ID, account.getId()); - PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, (int) account.getId(), deleteIntent, - PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentTitle(titleForType(context, body)) + .setContentText(bodyForType(body)); - final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, getChannelId(account, body)) - .setSmallIcon(R.drawable.ic_notify) - .setContentIntent(resultPendingIntent) - .setDeleteIntent(deletePendingIntent) - .setColor(ContextCompat.getColor(context, (R.color.primary))) - .setDefaults(0); // So it doesn't ring twice, notify only in Target callback + if (body.getType() == Notification.Type.MENTION) { + builder.setStyle(new NotificationCompat.BigTextStyle() + .bigText(bodyForType(body))); + } - setupPreferences(account, builder); + //load the avatar synchronously + Bitmap accountAvatar; + try { + accountAvatar = Picasso.with(context) + .load(body.getAccount().getAvatar()) + .transform(new RoundedTransformation(20)) + .get(); + } catch (IOException e) { + Log.d(TAG, "error loading account avatar", e); + accountAvatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar_default); + } - if (currentNotifications.length() == 1) { - builder.setContentTitle(titleForType(context, body)) - .setContentText(bodyForType(body)); + builder.setLargeIcon(accountAvatar); - if (body.getType() == Notification.Type.MENTION) { - builder.setStyle(new NotificationCompat.BigTextStyle() - .bigText(bodyForType(body))); - } + // Reply to mention action; RemoteInput is available from KitKat Watch, but buttons are available from Nougat + if (body.getType() == Notification.Type.MENTION + && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + RemoteInput replyRemoteInput = new RemoteInput.Builder(KEY_REPLY) + .setLabel(context.getString(R.string.label_quick_reply)) + .build(); - //load the avatar synchronously - Bitmap accountAvatar; - try { - accountAvatar = Picasso.with(context) - .load(body.getAccount().getAvatar()) - .transform(new RoundedTransformation(20)) - .get(); - } catch (IOException e) { - Log.d(TAG, "error loading account avatar", e); - accountAvatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar_default); - } + PendingIntent quickReplyPendingIntent = getStatusReplyIntent(REPLY_ACTION, context, body, account); - builder.setLargeIcon(accountAvatar); + NotificationCompat.Action quickReplyAction = + new NotificationCompat.Action.Builder(R.drawable.ic_reply_24dp, + context.getString(R.string.action_quick_reply), quickReplyPendingIntent) + .addRemoteInput(replyRemoteInput) + .build(); - } else { + builder.addAction(quickReplyAction); + + PendingIntent composePendingIntent = getStatusReplyIntent(COMPOSE_ACTION, context, body, account); + + NotificationCompat.Action composeAction = + new NotificationCompat.Action.Builder(R.drawable.ic_reply_24dp, + context.getString(R.string.action_compose_shortcut), composePendingIntent) + .build(); + + builder.addAction(composeAction); + } + + builder.setSubText(account.getFullName()); + builder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); + builder.setCategory(NotificationCompat.CATEGORY_SOCIAL); + builder.setOnlyAlertOnce(true); + + // Summary + // ======= + final NotificationCompat.Builder summaryBuilder = newNotification(context, body, account, true); + + if (currentNotifications.length() != 1) { try { String title = context.getString(R.string.notification_title_summary, currentNotifications.length()); String text = joinNames(context, currentNotifications); - builder.setContentTitle(title) + summaryBuilder.setContentTitle(title) .setContentText(text); } catch (JSONException e) { Log.d(TAG, Log.getStackTraceString(e)); } } - builder.setSubText(account.getFullName()); + summaryBuilder.setSubText(account.getFullName()); + summaryBuilder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); + summaryBuilder.setCategory(NotificationCompat.CATEGORY_SOCIAL); + summaryBuilder.setOnlyAlertOnce(true); + summaryBuilder.setGroupSummary(true); - builder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); - builder.setCategory(NotificationCompat.CATEGORY_SOCIAL); - - builder.setOnlyAlertOnce(true); - - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); //noinspection ConstantConditions - notificationManager.notify((int) account.getId(), builder.build()); + notificationManager.notify(notificationId, builder.build()); + if (currentNotifications.length() == 1) { + notificationManager.notify((int) account.getId(), builder.setGroupSummary(true).build()); + } else { + notificationManager.notify((int) account.getId(), summaryBuilder.build()); + } + } + + private static NotificationCompat.Builder newNotification(Context context, Notification body, AccountEntity account, boolean summary) { + Intent summaryResultIntent = new Intent(context, MainActivity.class); + summaryResultIntent.putExtra(ACCOUNT_ID, account.getId()); + TaskStackBuilder summaryStackBuilder = TaskStackBuilder.create(context); + summaryStackBuilder.addParentStack(MainActivity.class); + summaryStackBuilder.addNextIntent(summaryResultIntent); + + PendingIntent summaryResultPendingIntent = summaryStackBuilder.getPendingIntent(notificationId, + PendingIntent.FLAG_UPDATE_CURRENT); + + // we have to switch account here + Intent eventResultIntent = new Intent(context, ViewThreadActivity.class); + eventResultIntent.putExtra("account", account.getId()); + eventResultIntent.putExtra("id", body.getStatus().getId()); + TaskStackBuilder eventStackBuilder = TaskStackBuilder.create(context); + eventStackBuilder.addParentStack(ViewThreadActivity.class); + eventStackBuilder.addNextIntent(eventResultIntent); + + PendingIntent eventResultPendingIntent = eventStackBuilder.getPendingIntent((int) account.getId(), + PendingIntent.FLAG_UPDATE_CURRENT); + + Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class); + deleteIntent.putExtra(ACCOUNT_ID, account.getId()); + PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, summary ? (int) account.getId() : notificationId, deleteIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, getChannelId(account, body)) + .setSmallIcon(R.drawable.ic_notify) + .setContentIntent(summary ? summaryResultPendingIntent : eventResultPendingIntent) + .setDeleteIntent(deletePendingIntent) + .setColor(ContextCompat.getColor(context, (R.color.primary))) + .setGroup(account.getAccountId()) + .setDefaults(0); // So it doesn't ring twice, notify only in Target callback + + setupPreferences(account, builder); + + return builder; + } + + private static PendingIntent getStatusReplyIntent(String action, Context context, Notification body, AccountEntity account) { + Status status = body.getStatus(); + + String citedLocalAuthor = status.getAccount().getLocalUsername(); + String citedText = status.getContent().toString(); + String inReplyToId = status.getId(); + Status actionableStatus = status.getActionableStatus(); + Status.Visibility replyVisibility = actionableStatus.getVisibility(); + String contentWarning = actionableStatus.getSpoilerText(); + Status.Mention[] mentions = actionableStatus.getMentions(); + List mentionedUsernames = new ArrayList<>(); + mentionedUsernames.add(actionableStatus.getAccount().getUsername()); + for (Status.Mention mention : mentions) { + mentionedUsernames.add(mention.getUsername()); + } + mentionedUsernames.removeAll(Collections.singleton(account.getUsername())); + mentionedUsernames = new ArrayList<>(new LinkedHashSet<>(mentionedUsernames)); + + Intent replyIntent = new Intent(context, SendStatusBroadcastReceiver.class) + .setAction(action) + .putExtra(KEY_CITED_AUTHOR_LOCAL, citedLocalAuthor) + .putExtra(KEY_CITED_TEXT, citedText) + .putExtra(KEY_SENDER_ACCOUNT_ID, account.getId()) + .putExtra(KEY_SENDER_ACCOUNT_IDENTIFIER, account.getIdentifier()) + .putExtra(KEY_SENDER_ACCOUNT_FULL_NAME, account.getFullName()) + .putExtra(KEY_NOTIFICATION_ID, notificationId) + .putExtra(KEY_CITED_STATUS_ID, inReplyToId) + .putExtra(KEY_VISIBILITY, replyVisibility) + .putExtra(KEY_SPOILER, contentWarning) + .putExtra(KEY_MENTIONS, mentionedUsernames.toArray(new String[0])); + + return PendingIntent.getBroadcast(context.getApplicationContext(), + notificationId, + replyIntent, + PendingIntent.FLAG_UPDATE_CURRENT); } public static void createNotificationChannelsForAccount(AccountEntity account, Context context) { diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index b39d1448f..ac195a1bc 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -63,11 +63,14 @@ Dodatkowe komentarze? + Odpowiedz… + Szybka odpowiedź Odpowiedz Podbij Dodaj do ulubionych Więcej Napisz + Odpowiedz Zaloguj się Kontem Mastodon Wyloguj się Czy na pewno chcesz wylogować się z konta %1$s? @@ -318,5 +321,7 @@ Pobieranie %1$s… Skopiuj odnośnik + Wysłano! + Pomyślnie wysłano odpowiedź. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e61d62335..c3bcb8b0f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,6 +18,7 @@ The upload failed. At least one status must be reported. Invalid regular expression + Error sending toot. Home Advanced @@ -56,6 +57,7 @@ Report @%s Additional comments? + Quick Reply Reply Boost Favourite @@ -112,6 +114,9 @@ User unblocked User unmuted + Sent! + Reply sent successfully. + Which instance? What\'s happening? Content warning @@ -121,6 +126,7 @@ No results + Reply… Avatar Header @@ -295,11 +301,12 @@ Lock account Requires you to manually approve followers Save draft? - Sending Toot... + Sending Toot… Error sending toot Sending Toots Sending cancelled A copy of the toot has been saved to your drafts + Compose Your instance %s does not have any custom emojis Copied to clipboard