From ddc4954f8a01a7df09b7f71d74b0cf6a0351a421 Mon Sep 17 00:00:00 2001 From: Ivan Kupalov Date: Mon, 13 Nov 2017 21:05:23 +0300 Subject: [PATCH] Cancel notifications on opening notifications tab (#450) --- .../com/keylesspalace/tusky/BaseActivity.java | 9 -- .../keylesspalace/tusky/LoginActivity.java | 4 +- .../com/keylesspalace/tusky/MainActivity.java | 3 +- .../tusky/NotificationPullJobCreator.java | 6 +- .../tusky/fragment/NotificationsFragment.java | 31 +++++-- .../tusky/fragment/PreferencesFragment.java | 4 +- ...ionMaker.java => NotificationManager.java} | 84 +++++++++++-------- 7 files changed, 84 insertions(+), 57 deletions(-) rename app/src/main/java/com/keylesspalace/tusky/util/{NotificationMaker.java => NotificationManager.java} (82%) diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 011a3b0d..5d1c9196 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -187,15 +187,6 @@ public class BaseActivity extends AppCompatActivity { JobManager.instance().cancelAllForTag(NotificationPullJobCreator.NOTIFICATIONS_JOB_TAG); } - protected void clearNotifications() { - SharedPreferences notificationPreferences = - getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE); - notificationPreferences.edit().putString("current", "[]").apply(); - - NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - manager.cancel(NotificationPullJobCreator.NOTIFY_ID); - } - protected void setPullNotificationCheckInterval(long minutes) { JobManager.instance().cancelAllForTag(NotificationPullJobCreator.NOTIFICATIONS_JOB_TAG); long checkInterval = 1000 * 60 * minutes; diff --git a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java index b771582a..defa1ca4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java @@ -40,7 +40,7 @@ import com.keylesspalace.tusky.entity.AccessToken; import com.keylesspalace.tusky.entity.AppCredentials; import com.keylesspalace.tusky.network.MastodonApi; import com.keylesspalace.tusky.util.CustomTabsHelper; -import com.keylesspalace.tusky.util.NotificationMaker; +import com.keylesspalace.tusky.util.NotificationManager; import com.keylesspalace.tusky.util.OkHttpUtils; import java.util.HashMap; @@ -396,7 +396,7 @@ public class LoginActivity extends AppCompatActivity { } //create notification channels ahead of time so users can edit the settings - NotificationMaker.createNotificationChannels(this); + NotificationManager.createNotificationChannels(this); Intent intent = new Intent(this, MainActivity.class); startActivity(intent); diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index 2eebba34..ebb311ed 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -41,6 +41,7 @@ import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.interfaces.ActionButtonActivity; import com.keylesspalace.tusky.pager.TimelinePagerAdapter; import com.keylesspalace.tusky.receiver.TimelineReceiver; +import com.keylesspalace.tusky.util.NotificationManager; import com.keylesspalace.tusky.util.ThemeUtils; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.materialdrawer.AccountHeader; @@ -225,7 +226,7 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity { protected void onResume() { super.onResume(); - clearNotifications(); + NotificationManager.clearNotifications(this); /* After editing a profile, the profile header in the navigation drawer needs to be * refreshed */ diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java b/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java index df19b804..0ea4e69e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java +++ b/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java @@ -29,7 +29,7 @@ import com.keylesspalace.tusky.entity.Notification; import com.keylesspalace.tusky.json.SpannedTypeAdapter; import com.keylesspalace.tusky.network.AuthInterceptor; import com.keylesspalace.tusky.network.MastodonApi; -import com.keylesspalace.tusky.util.NotificationMaker; +import com.keylesspalace.tusky.util.NotificationManager; import com.keylesspalace.tusky.util.OkHttpUtils; import java.io.IOException; @@ -48,8 +48,8 @@ import retrofit2.converter.gson.GsonConverterFactory; public final class NotificationPullJobCreator implements JobCreator { + public static final int NOTIFY_ID = 6; // chosen by fair dice roll, guaranteed to be random static final String NOTIFICATIONS_JOB_TAG = "notifications_job_tag"; - static final int NOTIFY_ID = 6; // chosen by fair dice roll, guaranteed to be random private Context context; @@ -130,7 +130,7 @@ public final class NotificationPullJobCreator implements JobCreator { String id = notification.id; if (!currentIds.contains(id)) { currentIds.add(id); - NotificationMaker.make(context, NOTIFY_ID, notification); + NotificationManager.make(context, NOTIFY_ID, notification); } } notificationsPreferences.edit() diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index fe535fbe..a1a301ca 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -15,6 +15,8 @@ package com.keylesspalace.tusky.fragment; +import android.app.Activity; +import android.app.NotificationManager; import android.arch.core.util.Function; import android.content.Context; import android.content.SharedPreferences; @@ -36,6 +38,7 @@ import android.view.View; import android.view.ViewGroup; import com.keylesspalace.tusky.MainActivity; +import com.keylesspalace.tusky.NotificationPullJobCreator; import com.keylesspalace.tusky.adapter.FooterViewHolder; import com.keylesspalace.tusky.adapter.NotificationsAdapter; import com.keylesspalace.tusky.R; @@ -129,12 +132,12 @@ public class NotificationsFragment extends SFragment implements @Nullable @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_timeline, container, false); + @NonNull Context context = inflater.getContext(); // from inflater to silence warning // Setup the SwipeRefreshLayout. - Context context = getContext(); swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout); swipeRefreshLayout.setOnRefreshListener(this); // Setup the RecyclerView. @@ -176,6 +179,7 @@ public class NotificationsFragment extends SFragment implements super.onActivityCreated(savedInstanceState); MainActivity activity = (MainActivity) getActivity(); + if (activity == null) throw new AssertionError("Activity is null"); // MainActivity's layout is guaranteed to be inflated until onCreate returns. TabLayout layout = activity.findViewById(R.id.tab_layout); @@ -232,15 +236,30 @@ public class NotificationsFragment extends SFragment implements @Override public void onDestroyView() { - TabLayout tabLayout = getActivity().findViewById(R.id.tab_layout); - tabLayout.removeOnTabSelectedListener(onTabSelectedListener); + Activity activity = getActivity(); + if (activity == null) { + Log.e(TAG, "Activity is null"); + } else { + TabLayout tabLayout = activity.findViewById(R.id.tab_layout); + tabLayout.removeOnTabSelectedListener(onTabSelectedListener); - LocalBroadcastManager.getInstance(getContext()) - .unregisterReceiver(timelineReceiver); + LocalBroadcastManager.getInstance(activity) + .unregisterReceiver(timelineReceiver); + } super.onDestroyView(); } + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (isVisibleToUser) { + //noinspection ConstantConditions + ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)) + .cancel(NotificationPullJobCreator.NOTIFY_ID); + } + } + @Override public void onRefresh() { sendFetchNotificationsRequest(null, topId, FetchEnd.TOP, -1); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java index fccdf7ca..9e96d5ac 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java @@ -25,7 +25,7 @@ import android.support.annotation.XmlRes; import com.keylesspalace.tusky.BuildConfig; import com.keylesspalace.tusky.PreferencesActivity; import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.util.NotificationMaker; +import com.keylesspalace.tusky.util.NotificationManager; public class PreferencesFragment extends PreferenceFragment { @@ -56,7 +56,7 @@ public class PreferencesFragment extends PreferenceFragment { //on Android O and newer, launch the system notification settings instead of the app settings if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationMaker.createNotificationChannels(getContext()); + NotificationManager.createNotificationChannels(getContext()); notificationPreferences.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationManager.java similarity index 82% rename from app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java rename to app/src/main/java/com/keylesspalace/tusky/util/NotificationManager.java index 396da2a2..bf7f2ac5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationManager.java @@ -16,7 +16,6 @@ package com.keylesspalace.tusky.util; import android.app.NotificationChannel; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -33,6 +32,7 @@ import android.support.v4.content.ContextCompat; import android.util.Log; import com.keylesspalace.tusky.MainActivity; +import com.keylesspalace.tusky.NotificationPullJobCreator; import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Notification; import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver; @@ -46,22 +46,24 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -public class NotificationMaker { - private static final String TAG = "NotificationMaker"; +public class NotificationManager { + private static final String TAG = "NotificationManager"; - /** notification channels used on Android O+ **/ + /** + * 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"; + private static final String CHANNEL_FAVOURITE = " CHANNEL_FAVOURITE"; /** * Takes a given Mastodon notification and either creates a new Android notification or updates * the state of the existing notification to reflect the new interaction. * - * @param context to access application preferences and services + * @param context to access application preferences and services * @param notifyId an arbitrary number to reference this notification for any future action - * @param body a new Mastodon notification + * @param body a new Mastodon notification */ public static void make(final Context context, final int notifyId, Notification body) { final SharedPreferences preferences = @@ -86,7 +88,7 @@ public class NotificationMaker { boolean alreadyContains = false; - for(int i = 0; i < currentNotifications.length(); i++) { + for (int i = 0; i < currentNotifications.length(); i++) { try { if (currentNotifications.getString(i).equals(body.account.getDisplayName())) { alreadyContains = true; @@ -102,7 +104,7 @@ public class NotificationMaker { notificationPreferences.edit() .putString("current", currentNotifications.toString()) - .commit(); + .apply(); Intent resultIntent = new Intent(context, MainActivity.class); resultIntent.putExtra("tab_position", 1); @@ -160,16 +162,17 @@ public class NotificationMaker { builder.setCategory(android.app.Notification.CATEGORY_SOCIAL); } - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + android.app.NotificationManager notificationManager = (android.app.NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + //noinspection ConstantConditions notificationManager.notify(notifyId, builder.build()); } public static void createNotificationChannels(Context context) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationManager mNotificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + android.app.NotificationManager mNotificationManager = + (android.app.NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); String[] channelIds = new String[]{CHANNEL_MENTION, CHANNEL_FOLLOW, CHANNEL_BOOST, CHANNEL_FAVOURITE}; int[] channelNames = { @@ -187,11 +190,11 @@ public class NotificationMaker { List channels = new ArrayList<>(4); - for(int i=0; i= Build.VERSION_CODES.O) { + android.app.NotificationManager manager = (android.app.NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + //noinspection ConstantConditions + manager.cancel(NotificationPullJobCreator.NOTIFY_ID); + } + + private static boolean filterNotification(SharedPreferences preferences, + Notification notification) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return true; //do not filter on Android O or newer, the system does it for us } - switch (notification.type) { + switch (notification.type) { default: case MENTION: return preferences.getBoolean("notificationFilterMentions", true); @@ -227,20 +242,21 @@ public class NotificationMaker { } private static String getChannelId(Notification notification) { - switch (notification.type) { - default: - case MENTION: - return CHANNEL_MENTION; - case FOLLOW: - return CHANNEL_FOLLOW; - case REBLOG: - return CHANNEL_BOOST; - case FAVOURITE: - return CHANNEL_FAVOURITE; - } + switch (notification.type) { + default: + case MENTION: + return CHANNEL_MENTION; + case FOLLOW: + return CHANNEL_FOLLOW; + case REBLOG: + return CHANNEL_BOOST; + case FAVOURITE: + return CHANNEL_FAVOURITE; + } } + @SuppressWarnings("SameParameterValue") private static String truncateWithEllipses(String string, int limit) { if (string.length() < limit) { return string; @@ -250,9 +266,9 @@ public class NotificationMaker { } private static void setupPreferences(SharedPreferences preferences, - NotificationCompat.Builder builder) { + NotificationCompat.Builder builder) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return; //do nothing on Android O or newer, the system uses the channel settings anyway } @@ -261,7 +277,7 @@ public class NotificationMaker { } if (preferences.getBoolean("notificationAlertVibrate", false)) { - builder.setVibrate(new long[] { 500, 500 }); + builder.setVibrate(new long[]{500, 500}); } if (preferences.getBoolean("notificationAlertLight", false)) {