I'm currently planning to implement dynamic notification settings in my app. If the current device is running Android Oreo, show Notification Channel settings. If it's not Android Oreo, then show another setting (PreferenceCategory
)
Here's a snippet of my code used for displaying when the device's version is not Oreo:
for (MyNotificationChannel notificationChannel : notificationChannels) {
System.out.println("Notification channel created at index " + notificationChannel.getIndex());
PreferenceCategory preferenceCategory = new PreferenceCategory(preferenceScreen.getContext());
preferenceCategory.setTitle(notificationChannel.getNotificationTitle());
preferenceCategory.setSummary(notificationChannel.getNotificationDesc());
SwitchPreference enableNotificationPreference = new SwitchPreference(preferenceScreen.getContext());
enableNotificationPreference.setDefaultValue(true);
enableNotificationPreference.setTitle("Enable notification channel");
enableNotificationPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
System.out.println("Preference change: " + newValue);
return true;
}
});
preferenceCategory.addPreference(enableNotificationPreference);
SwitchPreference enableVibratePreference = new SwitchPreference(preferenceScreen.getContext());
enableVibratePreference.setTitle("Vibrate");
enableVibratePreference.setSummary("Whether to vibrate when there are notifications available");
enableVibratePreference.setDefaultValue(true);
enableVibratePreference.setIcon(R.drawable.ic_vibrate_white_24dp);
enableVibratePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
System.out.println("Preference change: " + newValue);
return true;
}
});
RingtonePreference notificationRingtonePreference = new RingtonePreference(preferenceScreen.getContext());
notificationRingtonePreference.setIcon(R.drawable.ic_music_white_24dp);
notificationRingtonePreference.setTitle("Set ringtone");
bindPreferenceSummaryToValue(notificationRingtonePreference);
preferenceCategory.addPreference(notificationRingtonePreference);
preferenceScreen.addPreference(preferenceCategory);
}
(By the way, MyNotificationChannel
is just a class meant as an interface for my notification settings to make it easier to get stuff from strings.xml
)
However, I'm getting this error when I attempt to navigate to the notification fragment:
java.lang.NullPointerException: Attempt to invoke virtual method 'long android.preference.PreferenceManager.getNextId()' on a null object reference
at android.preference.Preference.onAttachedToHierarchy(Preference.java:1326)
at android.preference.PreferenceGroup.addPreference(PreferenceGroup.java:163)
at com.edricchan.studybuddy.SettingsActivity$NotificationPreferenceFragment.onCreate(SettingsActivity.java:345)
at android.app.Fragment.performCreate(Fragment.java:2489)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1237)
at android.app.FragmentManagerImpl.addAddedFragments(FragmentManager.java:2407)
at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2186)
at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2142)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2043)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:719)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
EDIT: Here's my preferences file:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- This preference screen will be filled in via code -->
</PreferenceScreen>
EDIT #2: When I revert back to my original implementation (for Android Oreo), it works:
PreferenceCategory notificationPrefCategory = new PreferenceCategory(preferenceScreen.getContext());
notificationPrefCategory.setTitle("Notification Channels");
preferenceScreen.addPreference(notificationPrefCategory);
Preference allNotificationsPreference = new Preference(preferenceScreen.getContext());
allNotificationsPreference.setTitle(R.string.notification_channel_all_channels_title);
allNotificationsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getActivity().getPackageName());
startActivity(intent);
}
return false;
}
});
notificationPrefCategory.addPreference(allNotificationsPreference);
for (MyNotificationChannel notificationChannel : notificationChannels) {
Preference notificationPreference = new Preference(preferenceScreen.getContext());
notificationPreference.setTitle(notificationChannel.getNotificationTitle());
notificationPreference.setSummary(notificationChannel.getNotificationDesc());
notificationPreference.setKey(notificationChannelIds[notificationChannel.getIndex()]);
notificationPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getActivity().getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, preference.getKey());
startActivity(intent);
}
return false;
}
});
notificationPrefCategory.addPreference(notificationPreference);
}
I fixed the issue by moving up the line that adds the preference category to the preference screen to before all the other preferences are defined:
// This line
preferenceScreen.addPreference(preferenceCategory);
(P.S. I moved it to after I defined the PreferenceCategory
)