diff --git a/mastodon/src/main/java/org/joinmastodon/android/PushNotificationReceiver.java b/mastodon/src/main/java/org/joinmastodon/android/PushNotificationReceiver.java index 353e0d8fc..77b6112a5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/PushNotificationReceiver.java +++ b/mastodon/src/main/java/org/joinmastodon/android/PushNotificationReceiver.java @@ -23,6 +23,7 @@ import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited; import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.NotificationReceivedEvent; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.NotificationAction; import org.joinmastodon.android.model.Preferences; @@ -85,6 +86,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{ } String accountID=account.getID(); PushNotification pn=AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().decryptNotification(k, p, s); + E.post(new NotificationReceivedEvent(pn.notificationId+"")); new GetNotificationByID(pn.notificationId+"") .setCallback(new Callback<>(){ @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java index 8c588e468..a2cb79775 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java @@ -11,7 +11,7 @@ public class ApiUtils{ //no instance } - public static > List enumSetToStrings(EnumSet e, Class cls){ +public static > List enumSetToStrings(EnumSet e, Class cls){ return e.stream().map(ev->{ try{ SerializedName annotation=cls.getField(ev.name()).getAnnotation(SerializedName.class); diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/markers/GetMarkers.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/markers/GetMarkers.java new file mode 100644 index 000000000..b7dd6536b --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/markers/GetMarkers.java @@ -0,0 +1,17 @@ +package org.joinmastodon.android.api.requests.markers; + +import org.joinmastodon.android.api.ApiUtils; +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.model.Marker; +import org.joinmastodon.android.model.Markers; + +import java.util.EnumSet; + +public class GetMarkers extends MastodonAPIRequest { + public GetMarkers(EnumSet timelines) { + super(HttpMethod.GET, "/markers", Markers.class); + for (String type : ApiUtils.enumSetToStrings(timelines, Marker.Type.class)){ + addQueryParameter("timeline[]", type); + } + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java index e7fa19d9e..da32d9d1a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java @@ -7,6 +7,7 @@ import org.joinmastodon.android.api.StatusInteractionController; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Application; import org.joinmastodon.android.model.Filter; +import org.joinmastodon.android.model.Markers; import org.joinmastodon.android.model.Preferences; import org.joinmastodon.android.model.PushSubscription; import org.joinmastodon.android.model.Token; @@ -31,6 +32,7 @@ public class AccountSession{ public String pushAccountID; public Preferences preferences; public AccountActivationInfo activationInfo; + public Markers markers; private transient MastodonAPIController apiController; private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController; private transient CacheController cacheController; diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java index 1c3f6d808..465df2cfa 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java @@ -25,6 +25,7 @@ import org.joinmastodon.android.api.requests.accounts.GetWordFilters; import org.joinmastodon.android.api.requests.instance.GetCustomEmojis; import org.joinmastodon.android.api.requests.accounts.GetOwnAccount; import org.joinmastodon.android.api.requests.instance.GetInstance; +import org.joinmastodon.android.api.requests.markers.GetMarkers; import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp; import org.joinmastodon.android.events.EmojiUpdatedEvent; import org.joinmastodon.android.model.Account; @@ -33,6 +34,8 @@ import org.joinmastodon.android.model.Emoji; import org.joinmastodon.android.model.EmojiCategory; import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Instance; +import org.joinmastodon.android.model.Marker; +import org.joinmastodon.android.model.Markers; import org.joinmastodon.android.model.Preferences; import org.joinmastodon.android.model.Token; @@ -46,6 +49,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -255,6 +259,7 @@ public class AccountSessionManager{ // if(now-session.filtersLastUpdated>3600_000L){ updateSessionWordFilters(session); // } + updateSessionMarkers(session); } if(loadedInstances){ maybeUpdateCustomEmojis(domains); @@ -331,6 +336,21 @@ public class AccountSessionManager{ .exec(session.getID()); } + private void updateSessionMarkers(AccountSession session) { + new GetMarkers(EnumSet.allOf(Marker.Type.class)).setCallback(new Callback<>() { + @Override + public void onSuccess(Markers markers) { + session.markers = markers; + writeAccountsFile(); + } + + @Override + public void onError(ErrorResponse error) { + + } + }).exec(session.getID()); + } + public void updateInstanceInfo(String domain){ new GetInstance() .setCallback(new Callback<>(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/AllNotificationsSeenEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/AllNotificationsSeenEvent.java new file mode 100644 index 000000000..aded8546a --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/AllNotificationsSeenEvent.java @@ -0,0 +1,4 @@ +package org.joinmastodon.android.events; + +public class AllNotificationsSeenEvent { +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/NotificationReceivedEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/NotificationReceivedEvent.java new file mode 100644 index 000000000..0ea284f07 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/NotificationReceivedEvent.java @@ -0,0 +1,8 @@ +package org.joinmastodon.android.events; + +public class NotificationReceivedEvent { + public String id; + public NotificationReceivedEvent(String id) { + this.id = id; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index 323fa60f5..02b6883c6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -16,21 +16,34 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; +import org.joinmastodon.android.E; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.notifications.GetNotifications; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.AllNotificationsSeenEvent; +import org.joinmastodon.android.events.NotificationReceivedEvent; import org.joinmastodon.android.fragments.discover.DiscoverFragment; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Instance; +import org.joinmastodon.android.model.Notification; import org.joinmastodon.android.ui.AccountSwitcherSheet; import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.views.TabBar; import org.parceler.Parcels; import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; import androidx.annotation.IdRes; import androidx.annotation.Nullable; + +import com.squareup.otto.Subscribe; + import me.grishka.appkit.FragmentStackActivity; +import me.grishka.appkit.api.Callback; +import me.grishka.appkit.api.ErrorResponse; import me.grishka.appkit.fragments.AppKitFragment; import me.grishka.appkit.fragments.LoaderFragment; import me.grishka.appkit.fragments.OnBackPressedListener; @@ -48,6 +61,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene private TabBar tabBar; private View tabBarWrap; private ImageView tabBarAvatar; + private ImageView notificationTabIcon; @IdRes private int currentTab=R.id.tab_home; @@ -56,6 +70,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); + E.register(this); accountID=getArguments().getString("account"); setTitle(R.string.sk_app_name); @@ -108,6 +123,9 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene Account self=AccountSessionManager.getInstance().getAccount(accountID).self; ViewImageLoader.load(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(28), V.dp(28))); + notificationTabIcon=content.findViewById(R.id.tab_notifications); + updateNotificationBadge(); + if(savedInstanceState==null){ getChildFragmentManager().beginTransaction() .add(R.id.fragment_wrap, homeTabFragment) @@ -267,4 +285,49 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment); if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment); } + + public void updateNotificationBadge() { + AccountSession session = AccountSessionManager.getInstance().getAccount(accountID); + Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain); + + new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance.pleroma != null) + .setCallback(new Callback<>() { + @Override + public void onSuccess(List notifications) { + if (notifications.size() > 0) { + try { + long newestId = Long.parseLong(notifications.get(0).id); + long lastSeenId = Long.parseLong(session.markers.notifications.lastReadId); + System.out.println("NEWEST: " + newestId); + System.out.println("LAST SEEN: " + lastSeenId); + + setNotificationBadge(newestId > lastSeenId); + } catch (Exception ignored) { + setNotificationBadge(false); + } + } + } + + @Override + public void onError(ErrorResponse error) { + setNotificationBadge(false); + } + }).exec(accountID); + } + + public void setNotificationBadge(boolean badge) { + notificationTabIcon.setImageResource(badge + ? R.drawable.ic_fluent_alert_28_selector_badged + : R.drawable.ic_fluent_alert_28_selector); + } + + @Subscribe + public void onNotificationReceived(NotificationReceivedEvent notificationReceivedEvent) { + setNotificationBadge(true); + } + + @Subscribe + public void onAllNotificationsSeen(AllNotificationsSeenEvent allNotificationsSeenEvent) { + setNotificationBadge(false); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java index a95f061af..646311735 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java @@ -11,6 +11,7 @@ import org.joinmastodon.android.E; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.markers.SaveMarkers; import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.AllNotificationsSeenEvent; import org.joinmastodon.android.events.PollUpdatedEvent; import org.joinmastodon.android.events.RemoveAccountPostsEvent; import org.joinmastodon.android.model.Account; @@ -140,7 +141,11 @@ public class NotificationsListFragment extends BaseStatusListFragment + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_alert_28_regular_badged.xml b/mastodon/src/main/res/drawable/ic_fluent_alert_28_regular_badged.xml new file mode 100644 index 000000000..831e895fa --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_alert_28_regular_badged.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_alert_28_selector_badged.xml b/mastodon/src/main/res/drawable/ic_fluent_alert_28_selector_badged.xml new file mode 100644 index 000000000..3457f1fb5 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_alert_28_selector_badged.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mastodon/src/main/res/layout/tab_bar.xml b/mastodon/src/main/res/layout/tab_bar.xml index 2ad234cb5..70cd213c3 100644 --- a/mastodon/src/main/res/layout/tab_bar.xml +++ b/mastodon/src/main/res/layout/tab_bar.xml @@ -51,7 +51,6 @@ android:scaleType="center" android:contentDescription="@string/notifications" android:background="?android:selectableItemBackgroundBorderless" - android:tint="?android:colorPrimary" android:src="@drawable/ic_fluent_alert_28_selector"/>