mirror of https://github.com/ThmmyNoLife/mTHMMY
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
14 KiB
328 lines
14 KiB
package gr.thmmy.mthmmy.services;
|
|
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.graphics.Color;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.service.notification.StatusBarNotification;
|
|
|
|
import androidx.annotation.RequiresApi;
|
|
import androidx.core.app.NotificationCompat;
|
|
import androidx.preference.PreferenceManager;
|
|
|
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
|
import com.google.firebase.messaging.RemoteMessage;
|
|
|
|
import org.json.JSONException;
|
|
import org.json.JSONObject;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import gr.thmmy.mthmmy.R;
|
|
import gr.thmmy.mthmmy.activities.main.MainActivity;
|
|
import gr.thmmy.mthmmy.base.BaseApplication;
|
|
import gr.thmmy.mthmmy.model.Bookmark;
|
|
import gr.thmmy.mthmmy.model.PostNotification;
|
|
import timber.log.Timber;
|
|
|
|
import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
|
|
import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.NOTIFICATION_LED_KEY;
|
|
import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.NOTIFICATION_VIBRATION_KEY;
|
|
import static gr.thmmy.mthmmy.activities.settings.SettingsFragment.SELECTED_RINGTONE;
|
|
import static gr.thmmy.mthmmy.activities.settings.SettingsFragment.SETTINGS_SHARED_PREFS;
|
|
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE;
|
|
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
|
|
import static gr.thmmy.mthmmy.base.BaseActivity.BOOKMARKED_BOARDS_KEY;
|
|
import static gr.thmmy.mthmmy.base.BaseActivity.BOOKMARKED_TOPICS_KEY;
|
|
import static gr.thmmy.mthmmy.base.BaseActivity.BOOKMARKS_SHARED_PREFS;
|
|
import static gr.thmmy.mthmmy.model.Bookmark.matchExistsById;
|
|
|
|
public class NotificationService extends FirebaseMessagingService {
|
|
private static final int buildVersion = Build.VERSION.SDK_INT;
|
|
private static final int disabledNotifiationsLedColor = Color.argb(0, 0, 0, 0);
|
|
|
|
@Override
|
|
public void onMessageReceived(RemoteMessage remoteMessage) {
|
|
super.onMessageReceived(remoteMessage);
|
|
if (remoteMessage.getData().size() > 0) {
|
|
Timber.i("FCM data message received.");
|
|
JSONObject json = new JSONObject(remoteMessage.getData());
|
|
try {
|
|
int userId = BaseApplication.getInstance().getSessionManager().getUserId();
|
|
//Don't notify me if the sender is me!
|
|
if (Integer.parseInt(json.getString("posterId")) != userId) {
|
|
int boardId = -1;
|
|
String boardTitle = null;
|
|
int topicId = Integer.parseInt(json.getString("topicId"));
|
|
if (remoteMessage.getFrom().contains("b")) {
|
|
Timber.i("FCM BOARD type message detected.");
|
|
|
|
SharedPreferences bookmarksFile = getSharedPreferences(BOOKMARKS_SHARED_PREFS, Context.MODE_PRIVATE);
|
|
String bookmarkedTopicsString = bookmarksFile.getString(BOOKMARKED_TOPICS_KEY, null);
|
|
if (bookmarkedTopicsString != null && matchExistsById(Bookmark.stringToArrayList(bookmarkedTopicsString), topicId)) {
|
|
Timber.i("Board notification suppressed (already subscribed to topic).");
|
|
return;
|
|
}
|
|
|
|
boardId = Integer.parseInt(json.getString("boardId"));
|
|
|
|
String bookmarkedBoardsString = bookmarksFile.getString(BOOKMARKED_BOARDS_KEY, null);
|
|
if (bookmarkedBoardsString != null) {
|
|
ArrayList<Bookmark> boardBookmarks = Bookmark.stringToArrayList(bookmarkedBoardsString);
|
|
ArrayList<Integer> subBoardIds = getSubBoardIds(json.getString("boardIds"), boardId);
|
|
//TODO: Also suppress if user has chosen to be notified only for direct children of boardId && !subBoardIds.isEmpty()
|
|
for (int subId : subBoardIds) {
|
|
if (matchExistsById(boardBookmarks, subId)) {
|
|
Timber.i("Board notification suppressed (already subscribed to a subBoard).");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
boardTitle = json.getString("boardTitle");
|
|
}
|
|
else
|
|
Timber.i("FCM TOPIC type message detected.");
|
|
|
|
int postId = Integer.parseInt(json.getString("postId"));
|
|
String topicTitle = json.getString("topicTitle");
|
|
String poster = json.getString("poster");
|
|
|
|
sendNotification(new PostNotification(postId, topicId, topicTitle, poster, boardId, boardTitle));
|
|
}
|
|
else
|
|
Timber.i("Notification suppressed (own userID).");
|
|
} catch (JSONException e) {
|
|
Timber.e(e, "JSON Exception");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static ArrayList<Integer> getSubBoardIds(String boardIdsString, int boardId) {
|
|
ArrayList<Integer> subBoardIds = new ArrayList<>();
|
|
Pattern p = Pattern.compile("(\\d+)");
|
|
Matcher m = p.matcher(boardIdsString);
|
|
boolean boardIdfound = false;
|
|
while (m.find()) {
|
|
int subBoardId = Integer.parseInt(m.group());
|
|
if (boardIdfound)
|
|
subBoardIds.add(subBoardId);
|
|
else if (boardId == subBoardId)
|
|
boardIdfound = true;
|
|
}
|
|
return subBoardIds;
|
|
}
|
|
|
|
private static final String CHANNEL_ID = "Posts";
|
|
private static final String CHANNEL_NAME = "New Posts";
|
|
private static final String GROUP_KEY = "PostsGroup";
|
|
private static int requestCode = 0;
|
|
|
|
private static final String NEW_POSTS_COUNT = "newPostsCount";
|
|
public static final String NEW_POST_TAG = "NEW_POST";
|
|
private static final String SUMMARY_TAG = "SUMMARY";
|
|
|
|
/**
|
|
* Create and show a new post notification.
|
|
*/
|
|
private void sendNotification(PostNotification postNotification) {
|
|
Timber.i("Creating a notification...");
|
|
|
|
boolean isTopicNotification = postNotification.getBoardId() == -1;
|
|
|
|
//Reads notifications preferences
|
|
SharedPreferences settingsFile = getSharedPreferences(SETTINGS_SHARED_PREFS, Context.MODE_PRIVATE);
|
|
String notificationsSound = settingsFile.getString(SELECTED_RINGTONE, null);
|
|
Uri notificationSoundUri = notificationsSound != null ? Uri.parse(notificationsSound) : null;
|
|
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
|
boolean notificationsLedEnabled = sharedPrefs.getBoolean(NOTIFICATION_LED_KEY, true);
|
|
boolean notificationsVibrateEnabled = sharedPrefs.getBoolean(NOTIFICATION_VIBRATION_KEY, true);
|
|
int notificationDefaultValues = -1;
|
|
|
|
if (notificationsLedEnabled) {
|
|
notificationDefaultValues = Notification.DEFAULT_LIGHTS;
|
|
}
|
|
if (notificationsVibrateEnabled) {
|
|
if (notificationDefaultValues != -1) {
|
|
notificationDefaultValues |= Notification.DEFAULT_VIBRATE;
|
|
}
|
|
else {
|
|
notificationDefaultValues = Notification.DEFAULT_VIBRATE;
|
|
}
|
|
|
|
}
|
|
if (notificationSoundUri == null) {
|
|
if (notificationDefaultValues != -1) {
|
|
notificationDefaultValues = Notification.DEFAULT_SOUND;
|
|
}
|
|
else {
|
|
notificationDefaultValues |= Notification.DEFAULT_SOUND;
|
|
}
|
|
}
|
|
|
|
//Builds notification
|
|
String topicUrl = "https://www.thmmy.gr/smf/index.php?topic=" + postNotification.getTopicId() + "." + postNotification.getPostId();
|
|
Intent intent = new Intent(this, MainActivity.class);
|
|
Bundle extras = new Bundle();
|
|
extras.putString(BUNDLE_TOPIC_URL, topicUrl);
|
|
extras.putString(BUNDLE_TOPIC_TITLE, postNotification.getTopicTitle());
|
|
intent.putExtras(extras);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
int pendingIntentFlags;
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S)
|
|
pendingIntentFlags = PendingIntent.FLAG_IMMUTABLE |PendingIntent.FLAG_ONE_SHOT;
|
|
else
|
|
pendingIntentFlags = PendingIntent.FLAG_ONE_SHOT;
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestCode++, intent,
|
|
pendingIntentFlags);
|
|
|
|
int notificationId;
|
|
String contentText;
|
|
if (isTopicNotification) {
|
|
notificationId = postNotification.getTopicId();
|
|
contentText = "New post by " + postNotification.getPoster();
|
|
}
|
|
else {
|
|
// Using Cantor pairing function (plus the minus sign) for id uniqueness
|
|
int k1 = postNotification.getTopicId();
|
|
int k2 = postNotification.getBoardId();
|
|
notificationId = -(((k1 + k2) * (k1 + k2 + 1)) / 2 + k2);
|
|
contentText = "New post in \"" + postNotification.getTopicTitle() + "\"";
|
|
}
|
|
|
|
int newPostsCount = 1;
|
|
|
|
if (buildVersion >= Build.VERSION_CODES.M) {
|
|
Notification existingNotification = getActiveNotification(notificationId);
|
|
if (existingNotification != null) {
|
|
newPostsCount = existingNotification.extras.getInt(NEW_POSTS_COUNT) + 1;
|
|
if (isTopicNotification)
|
|
contentText = newPostsCount + " new posts";
|
|
else
|
|
contentText = newPostsCount + " new posts in " + postNotification.getTopicTitle();
|
|
}
|
|
}
|
|
|
|
Bundle notificationExtras = new Bundle();
|
|
notificationExtras.putInt(NEW_POSTS_COUNT, newPostsCount);
|
|
|
|
NotificationCompat.Builder notificationBuilder =
|
|
new NotificationCompat.Builder(this, CHANNEL_ID)
|
|
.setSmallIcon(R.drawable.ic_notification)
|
|
.setContentText(contentText)
|
|
.setAutoCancel(true)
|
|
.setContentIntent(pendingIntent)
|
|
.setGroup(GROUP_KEY)
|
|
.addExtras(notificationExtras);
|
|
|
|
if (isTopicNotification)
|
|
notificationBuilder.setContentTitle(postNotification.getTopicTitle());
|
|
|
|
else
|
|
notificationBuilder.setContentTitle(postNotification.getBoardTitle());
|
|
|
|
//Applies user's notifications preferences
|
|
if (notificationDefaultValues != -1) {
|
|
notificationBuilder.setDefaults(notificationDefaultValues);
|
|
}
|
|
if (notificationSoundUri != null) {
|
|
notificationBuilder.setSound(notificationSoundUri);
|
|
}
|
|
if (!notificationsVibrateEnabled) {
|
|
notificationBuilder.setVibrate(new long[]{0L});
|
|
}
|
|
if (!notificationsLedEnabled) {
|
|
notificationBuilder.setLights(disabledNotifiationsLedColor, 0, 1000);
|
|
}
|
|
|
|
if (buildVersion < Build.VERSION_CODES.O)
|
|
notificationBuilder.setPriority(PRIORITY_MAX);
|
|
|
|
boolean createSummaryNotification = false;
|
|
if (buildVersion >= Build.VERSION_CODES.M)
|
|
createSummaryNotification = otherNotificationsExist(notificationId);
|
|
|
|
|
|
NotificationCompat.Builder summaryNotificationBuilder = null;
|
|
if (createSummaryNotification) {
|
|
summaryNotificationBuilder =
|
|
new NotificationCompat.Builder(this, CHANNEL_ID)
|
|
.setSmallIcon(R.drawable.ic_notification)
|
|
.setGroupSummary(true)
|
|
.setGroup(GROUP_KEY)
|
|
.setAutoCancel(true)
|
|
.setStyle(new NotificationCompat.InboxStyle()
|
|
.setSummaryText("New Posts"))
|
|
.setDefaults(Notification.DEFAULT_ALL);
|
|
}
|
|
|
|
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
// Since Android Oreo notification channel is needed.
|
|
if (buildVersion >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(CHANNEL_ID) == null)
|
|
notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH));
|
|
|
|
|
|
notificationManager.notify(NEW_POST_TAG, notificationId, notificationBuilder.build());
|
|
|
|
if (createSummaryNotification)
|
|
notificationManager.notify(SUMMARY_TAG, 0, summaryNotificationBuilder.build());
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
private Notification getActiveNotification(int notificationId) {
|
|
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
if (notificationManager != null) {
|
|
StatusBarNotification[] barNotifications = notificationManager.getActiveNotifications();
|
|
for (StatusBarNotification notification : barNotifications) {
|
|
if (notification.getId() == notificationId)
|
|
return notification.getNotification();
|
|
}
|
|
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
private boolean otherNotificationsExist(int notificationId) {
|
|
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
if (notificationManager != null) {
|
|
StatusBarNotification[] barNotifications = notificationManager.getActiveNotifications();
|
|
for (StatusBarNotification notification : barNotifications) {
|
|
String tag = notification.getTag();
|
|
if (tag != null && tag.equals(NEW_POST_TAG) && notification.getId() != notificationId)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void onDeletedMessages() {
|
|
super.onDeletedMessages();
|
|
Timber.w("onDeletedMessages");
|
|
}
|
|
|
|
|
|
/**
|
|
* Called if InstanceID token is updated. Note that this is called when the InstanceID token
|
|
* is initially generated so this is where you would retrieve the token.
|
|
*/
|
|
@Override
|
|
public void onNewToken(String s) {
|
|
super.onNewToken(s);
|
|
Timber.i("InstanceID token updated (onNewToken)");
|
|
}
|
|
}
|
|
|