diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 3fd55a93..3b045c18 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -399,11 +399,18 @@ public class ComposeActivity extends BaseActivity { if (intent != null) { inReplyToId = intent.getStringExtra("in_reply_to_id"); String replyVisibility = intent.getStringExtra("reply_visibility"); + if (replyVisibility != null) { - /* Override any remembered visibilty and instead adopt the visibility of the status - * to which this replies. */ - statusVisibility = replyVisibility; + // Lowest possible visibility setting in response + if (statusVisibility.equals("private") || replyVisibility.equals("private")) { + statusVisibility = "private"; + } else if (statusVisibility.equals("unlisted") || replyVisibility.equals("unlisted")) { + statusVisibility = "unlisted"; + } else { + statusVisibility = replyVisibility; + } } + mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames"); } diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index bd54f6ad..a752ce4b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -15,6 +15,7 @@ package com.keylesspalace.tusky; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -22,12 +23,12 @@ import android.graphics.PorterDuff; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.PersistableBundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; -import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; @@ -187,6 +188,18 @@ public class MainActivity extends BaseActivity { }); } + @Override + protected void onResume() { + super.onResume(); + + SharedPreferences notificationPreferences = getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE); + SharedPreferences.Editor editor = notificationPreferences.edit(); + editor.putString("current", "[]"); + editor.apply(); + + ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).cancel(MyFirebaseMessagingService.NOTIFY_ID); + } + @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { ArrayList pageHistoryList = new ArrayList<>(); diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java index 5d1f1e92..3449f3f9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java +++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java @@ -22,6 +22,9 @@ import com.keylesspalace.tusky.entity.Notification; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; +import org.json.JSONArray; +import org.json.JSONException; + import java.io.IOException; import okhttp3.Interceptor; @@ -36,6 +39,7 @@ import retrofit2.converter.gson.GsonConverterFactory; public class MyFirebaseMessagingService extends FirebaseMessagingService { private MastodonAPI mastodonAPI; private static final String TAG = "MyFirebaseMessagingService"; + public static final int NOTIFY_ID = 666; @Override public void onMessageReceived(RemoteMessage remoteMessage) { @@ -112,6 +116,34 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { private void buildNotification(Notification body) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences notificationPreferences = getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE); + + String rawCurrentNotifications = notificationPreferences.getString("current", "[]"); + JSONArray currentNotifications; + + try { + currentNotifications = new JSONArray(rawCurrentNotifications); + } catch (JSONException e) { + currentNotifications = new JSONArray(); + } + + boolean alreadyContains = false; + + for(int i = 0; i < currentNotifications.length(); i++) { + try { + if (currentNotifications.getString(i).equals(body.account.displayName)) { + alreadyContains = true; + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + if (!alreadyContains) currentNotifications.put(body.account.displayName); + + SharedPreferences.Editor editor = notificationPreferences.edit(); + editor.putString("current", currentNotifications.toString()); + editor.commit(); Intent resultIntent = new Intent(this, MainActivity.class); resultIntent.putExtra("tab_position", 1); @@ -122,73 +154,103 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { final NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_notify) - .setAutoCancel(true) .setContentIntent(resultPendingIntent) .setDefaults(0); // So it doesn't ring twice, notify only in Target callback - final Integer mId = (int)(System.currentTimeMillis() / 1000); + if (currentNotifications.length() == 1) { + builder.setContentTitle(titleForType(body)) + .setContentText(truncateWithEllipses(bodyForType(body), 40)); - Target mTarget = new Target() { - @Override - public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { - builder.setLargeIcon(bitmap); + Target mTarget = new Target() { + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + builder.setLargeIcon(bitmap); - if (preferences.getBoolean("notificationAlertSound", true)) { - builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); + if (preferences.getBoolean("notificationAlertSound", true)) { + builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); + } + + if (preferences.getBoolean("notificationStyleVibrate", false)) { + builder.setVibrate(new long[] { 500, 500 }); + } + + if (preferences.getBoolean("notificationStyleLight", false)) { + builder.setLights(0xFF00FF8F, 300, 1000); + } + + ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(NOTIFY_ID, builder.build()); } - if (preferences.getBoolean("notificationStyleVibrate", false)) { - builder.setVibrate(new long[] { 500, 500 }); + @Override + public void onBitmapFailed(Drawable errorDrawable) { + } - if (preferences.getBoolean("notificationStyleLight", false)) { - builder.setLights(0xFF00FF8F, 300, 1000); + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + } + }; - ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build()); + Picasso.with(this) + .load(body.account.avatar) + .placeholder(R.drawable.avatar_default) + .transform(new RoundedTransformation(7, 0)) + .into(mTarget); + } else { + try { + builder.setContentTitle(String.format(getString(R.string.notification_title_summary), currentNotifications.length())) + .setContentText(truncateWithEllipses(joinNames(currentNotifications), 40)); + } catch (JSONException e) { + e.printStackTrace(); } - - @Override - public void onBitmapFailed(Drawable errorDrawable) { - - } - - @Override - public void onPrepareLoad(Drawable placeHolderDrawable) { - - } - }; - - Picasso.with(this) - .load(body.account.avatar) - .placeholder(R.drawable.avatar_default) - .transform(new RoundedTransformation(7, 0)) - .into(mTarget); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE); builder.setCategory(android.app.Notification.CATEGORY_SOCIAL); } - switch (body.type) { - case MENTION: - builder.setContentTitle(String.format(getString(R.string.notification_mention_format), body.account.getDisplayName())) - .setContentText(truncateWithEllipses(body.status.content.toString(), 40)); - break; - case FOLLOW: - builder.setContentTitle(String.format(getString(R.string.notification_follow_format), body.account.getDisplayName())) - .setContentText(truncateWithEllipses(body.account.username, 40)); - break; - case FAVOURITE: - builder.setContentTitle(String.format(getString(R.string.notification_favourite_format), body.account.getDisplayName())) - .setContentText(truncateWithEllipses(body.status.content.toString(), 40)); - break; - case REBLOG: - builder.setContentTitle(String.format(getString(R.string.notification_reblog_format), body.account.getDisplayName())) - .setContentText(truncateWithEllipses(body.status.content.toString(), 40)); - break; + ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(NOTIFY_ID, builder.build()); + } + + private String joinNames(JSONArray array) throws JSONException { + if (array.length() > 3) { + return String.format(getString(R.string.notification_summary_large), array.get(0), array.get(1), array.get(2), array.length() - 3); + } else if (array.length() == 3) { + return String.format(getString(R.string.notification_summary_medium), array.get(0), array.get(1), array.get(2)); + } else if (array.length() == 2) { + return String.format(getString(R.string.notification_summary_small), array.get(0), array.get(1)); } - ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build()); + return null; + } + + private String titleForType(Notification notification) { + switch (notification.type) { + case MENTION: + return String.format(getString(R.string.notification_mention_format), notification.account.getDisplayName()); + case FOLLOW: + return String.format(getString(R.string.notification_follow_format), notification.account.getDisplayName()); + case FAVOURITE: + return String.format(getString(R.string.notification_favourite_format), notification.account.getDisplayName()); + case REBLOG: + return String.format(getString(R.string.notification_reblog_format), notification.account.getDisplayName()); + } + + return null; + } + + private String bodyForType(Notification notification) { + switch (notification.type) { + case FOLLOW: + return notification.account.username; + case MENTION: + case FAVOURITE: + case REBLOG: + return notification.status.content.toString(); + } + + return null; } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1a3957b2..b2fac334 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,12 +102,8 @@ Everyone can see, but not on public timelines Only followers and mentions can see - Allows Tusky to check for Mastodon notifications. - %d new mentions - Mention from %s - Notifications - Enable pull notifcations + Enable pull notifications Check for notifications periodically Check interval How often to pull @@ -133,5 +129,8 @@ %s mentioned you Invalid domain entered This app could not obtain authentication from that server instance. - + %1$s, %2$s, %3$s and %4$d others + %1$s, %2$s, and %3$s + %1$s and %2$s + %d new interactions