1package eu.siacs.conversations.ui.fragment.settings;
2
3import android.app.NotificationManager;
4import android.content.ActivityNotFoundException;
5import android.content.Intent;
6import android.media.RingtoneManager;
7import android.net.Uri;
8import android.os.Build;
9import android.os.Bundle;
10import android.provider.Settings;
11import android.util.Log;
12import android.widget.Toast;
13
14import androidx.activity.result.ActivityResultLauncher;
15import androidx.annotation.NonNull;
16import androidx.annotation.Nullable;
17import androidx.preference.Preference;
18
19import com.google.common.base.Optional;
20
21import eu.siacs.conversations.AppSettings;
22import eu.siacs.conversations.Config;
23import eu.siacs.conversations.R;
24import eu.siacs.conversations.services.NotificationService;
25import eu.siacs.conversations.ui.activity.result.PickRingtone;
26import eu.siacs.conversations.utils.Compatibility;
27
28public class NotificationsSettingsFragment extends XmppPreferenceFragment {
29
30 private final ActivityResultLauncher<Uri> pickNotificationToneLauncher =
31 registerForActivityResult(
32 new PickRingtone(RingtoneManager.TYPE_NOTIFICATION),
33 result -> {
34 if (result == null) {
35 // do nothing. user aborted
36 return;
37 }
38 final Uri uri = PickRingtone.noneToNull(result);
39 appSettings().setNotificationTone(uri);
40 Log.i(Config.LOGTAG, "User set notification tone to " + uri);
41 });
42 private final ActivityResultLauncher<Uri> pickRingtoneLauncher =
43 registerForActivityResult(
44 new PickRingtone(RingtoneManager.TYPE_RINGTONE),
45 result -> {
46 if (result == null) {
47 // do nothing. user aborted
48 return;
49 }
50 final Uri uri = PickRingtone.noneToNull(result);
51 appSettings().setRingtone(uri);
52 Log.i(Config.LOGTAG, "User set ringtone to " + uri);
53 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
54 NotificationService.recreateIncomingCallChannel(requireContext(), uri);
55 }
56 });
57
58 @Override
59 public void onCreatePreferences(
60 @Nullable final Bundle savedInstanceState, final @Nullable String rootKey) {
61 setPreferencesFromResource(R.xml.preferences_notifications, rootKey);
62 final var messageNotificationSettings = findPreference("message_notification_settings");
63 final var fullscreenNotification = findPreference("fullscreen_notification");
64 final var notificationRingtone = findPreference(AppSettings.NOTIFICATION_RINGTONE);
65 final var notificationHeadsUp = findPreference(AppSettings.NOTIFICATION_HEADS_UP);
66 final var notificationVibrate = findPreference(AppSettings.NOTIFICATION_VIBRATE);
67 final var notificationLed = findPreference(AppSettings.NOTIFICATION_LED);
68 final var foregroundService = findPreference(AppSettings.KEEP_FOREGROUND_SERVICE);
69 if (messageNotificationSettings == null
70 || fullscreenNotification == null
71 || notificationRingtone == null
72 || notificationHeadsUp == null
73 || notificationVibrate == null
74 || notificationLed == null
75 || foregroundService == null) {
76 throw new IllegalStateException("The preference resource file is missing preferences");
77 }
78 if (Compatibility.runsTwentySix()) {
79 notificationRingtone.setVisible(false);
80 notificationHeadsUp.setVisible(false);
81 notificationVibrate.setVisible(false);
82 notificationLed.setVisible(false);
83 foregroundService.setVisible(false);
84 } else {
85 messageNotificationSettings.setVisible(false);
86 }
87 fullscreenNotification.setOnPreferenceClickListener(this::manageAppUseFullScreen);
88 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
89 || requireContext()
90 .getSystemService(NotificationManager.class)
91 .canUseFullScreenIntent()) {
92 fullscreenNotification.setVisible(false);
93 }
94 }
95
96 @Override
97 public void onResume() {
98 super.onResume();
99 final var fullscreenNotification = findPreference("fullscreen_notification");
100 if (fullscreenNotification == null) {
101 return;
102 }
103 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
104 || requireContext()
105 .getSystemService(NotificationManager.class)
106 .canUseFullScreenIntent()) {
107 fullscreenNotification.setVisible(false);
108 }
109 }
110
111 private boolean manageAppUseFullScreen(final Preference preference) {
112 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
113 return false;
114 }
115 final var intent = new Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT);
116 intent.setData(Uri.parse(String.format("package:%s", requireContext().getPackageName())));
117 try {
118 startActivity(intent);
119 } catch (final ActivityNotFoundException e) {
120 Toast.makeText(requireContext(), R.string.no_application_found, Toast.LENGTH_SHORT)
121 .show();
122 return false;
123 }
124 return true;
125 }
126
127 @Override
128 protected void onSharedPreferenceChanged(@NonNull String key) {
129 super.onSharedPreferenceChanged(key);
130 if (key.equals(AppSettings.KEEP_FOREGROUND_SERVICE)) {
131 requireService().toggleForegroundService();
132 }
133 }
134
135 @Override
136 public void onStart() {
137 super.onStart();
138 requireActivity().setTitle(R.string.notifications);
139 }
140
141 @Override
142 public boolean onPreferenceTreeClick(final Preference preference) {
143 final var key = preference.getKey();
144 if (AppSettings.RINGTONE.equals(key)) {
145 pickRingtone();
146 return true;
147 }
148 if (AppSettings.NOTIFICATION_RINGTONE.equals(key)) {
149 pickNotificationTone();
150 return true;
151 }
152 return super.onPreferenceTreeClick(preference);
153 }
154
155 private void pickNotificationTone() {
156 final Uri uri = appSettings().getNotificationTone();
157 Log.i(Config.LOGTAG, "current notification tone: " + uri);
158 this.pickNotificationToneLauncher.launch(uri);
159 }
160
161 private void pickRingtone() {
162 final Optional<Uri> channelRingtone;
163 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
164 channelRingtone =
165 NotificationService.getCurrentIncomingCallChannel(requireContext())
166 .transform(channel -> PickRingtone.nullToNone(channel.getSound()));
167 } else {
168 channelRingtone = Optional.absent();
169 }
170 final Uri uri;
171 if (channelRingtone.isPresent()) {
172 uri = channelRingtone.get();
173 Log.d(Config.LOGTAG, "ringtone came from channel");
174 } else {
175 uri = appSettings().getRingtone();
176 }
177 Log.i(Config.LOGTAG, "current ringtone: " + uri);
178 this.pickRingtoneLauncher.launch(uri);
179 }
180
181 private AppSettings appSettings() {
182 return new AppSettings(requireContext());
183 }
184}