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
This commit is contained in:
parent
aa48acdbec
commit
e8c79cce65
|
@ -101,6 +101,11 @@
|
|||
|
||||
<receiver android:name=".receiver.NotificationClearBroadcastReceiver" />
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.SendStatusBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
tools:targetApi="24"
|
||||
android:name="com.keylesspalace.tusky.service.TuskyTileService"
|
||||
|
|
|
@ -88,8 +88,6 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
|
|||
@Inject
|
||||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public AccountManager accountManager;
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||
|
||||
private String accountId;
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -167,8 +167,6 @@ public final class ComposeActivity
|
|||
|
||||
@Inject
|
||||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public AccountManager accountManager;
|
||||
|
||||
private TextView replyTextView;
|
||||
private TextView replyContentTextView;
|
||||
|
|
|
@ -92,8 +92,6 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity,
|
|||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> fragmentInjector;
|
||||
@Inject
|
||||
public AccountManager accountManager;
|
||||
|
||||
private static int COMPOSE_RESULT = 1;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
/* Copyright 2018 Jeremiasz Nelz <remi6397(a)gmail.com>
|
||||
* 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
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/* Copyright 2018 Jeremiasz Nelz <remi6397(a)gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses>. */
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
/* Copyright 2018 Jeremiasz Nelz <remi6397(a)gmail.com>
|
||||
* 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<String> 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) {
|
||||
|
|
|
@ -63,11 +63,14 @@
|
|||
<string name="report_comment_hint">Dodatkowe komentarze?</string>
|
||||
|
||||
|
||||
<string name="label_quick_reply">Odpowiedz…</string>
|
||||
<string name="action_quick_reply">Szybka odpowiedź</string>
|
||||
<string name="action_reply">Odpowiedz</string>
|
||||
<string name="action_reblog">Podbij</string>
|
||||
<string name="action_favourite">Dodaj do ulubionych</string>
|
||||
<string name="action_more">Więcej</string>
|
||||
<string name="action_compose">Napisz</string>
|
||||
<string name="action_compose_shortcut">Odpowiedz</string>
|
||||
<string name="action_login">Zaloguj się Kontem Mastodon</string>
|
||||
<string name="action_logout">Wyloguj się</string>
|
||||
<string name="action_logout_confirm">Czy na pewno chcesz wylogować się z konta %1$s?</string>
|
||||
|
@ -318,5 +321,7 @@
|
|||
<string name="download_image">Pobieranie %1$s…</string>
|
||||
|
||||
<string name="action_copy_link">Skopiuj odnośnik</string>
|
||||
<string name="status_sent">Wysłano!</string>
|
||||
<string name="status_sent_long">Pomyślnie wysłano odpowiedź.</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<string name="error_media_upload_sending">The upload failed.</string>
|
||||
<string name="error_report_too_few_statuses">At least one status must be reported.</string>
|
||||
<string name="error_invalid_regex">Invalid regular expression</string>
|
||||
<string name="error_sender_account_gone">Error sending toot.</string>
|
||||
|
||||
<string name="title_home">Home</string>
|
||||
<string name="title_advanced">Advanced</string>
|
||||
|
@ -56,6 +57,7 @@
|
|||
<string name="report_username_format">Report @%s</string>
|
||||
<string name="report_comment_hint">Additional comments?</string>
|
||||
|
||||
<string name="action_quick_reply">Quick Reply</string>
|
||||
<string name="action_reply">Reply</string>
|
||||
<string name="action_reblog">Boost</string>
|
||||
<string name="action_favourite">Favourite</string>
|
||||
|
@ -112,6 +114,9 @@
|
|||
<string name="confirmation_unblocked">User unblocked</string>
|
||||
<string name="confirmation_unmuted">User unmuted</string>
|
||||
|
||||
<string name="status_sent">Sent!</string>
|
||||
<string name="status_sent_long">Reply sent successfully.</string>
|
||||
|
||||
<string name="hint_domain">Which instance?</string>
|
||||
<string name="hint_compose">What\'s happening?</string>
|
||||
<string name="hint_content_warning">Content warning</string>
|
||||
|
@ -121,6 +126,7 @@
|
|||
|
||||
<string name="search_no_results">No results</string>
|
||||
|
||||
<string name="label_quick_reply">Reply…</string>
|
||||
<string name="label_avatar">Avatar</string>
|
||||
<string name="label_header">Header</string>
|
||||
|
||||
|
@ -295,11 +301,12 @@
|
|||
<string name="lock_account_label">Lock account</string>
|
||||
<string name="lock_account_label_description">Requires you to manually approve followers</string>
|
||||
<string name="compose_save_draft">Save draft?</string>
|
||||
<string name="send_toot_notification_title">Sending Toot...</string>
|
||||
<string name="send_toot_notification_title">Sending Toot…</string>
|
||||
<string name="send_toot_notification_error_title">Error sending toot</string>
|
||||
<string name="send_toot_notification_channel_name">Sending Toots</string>
|
||||
<string name="send_toot_notification_cancel_title">Sending cancelled</string>
|
||||
<string name="send_toot_notification_saved_content">A copy of the toot has been saved to your drafts</string>
|
||||
<string name="action_compose_shortcut">Compose</string>
|
||||
|
||||
<string name="error_no_custom_emojis">Your instance %s does not have any custom emojis</string>
|
||||
<string name="copy_to_clipboard_success">Copied to clipboard</string>
|
||||
|
|
Loading…
Reference in New Issue