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;
13import androidx.activity.result.ActivityResultLauncher;
14import androidx.annotation.NonNull;
15import androidx.annotation.Nullable;
16import androidx.preference.Preference;
17import androidx.preference.ListPreference;
18
19import com.google.common.base.Optional;
20import eu.siacs.conversations.AppSettings;
21import eu.siacs.conversations.Config;
22import eu.siacs.conversations.R;
23import eu.siacs.conversations.services.CallIntegration;
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 chatRequests = (ListPreference) findPreference("chat_requests");
69 final var foregroundService = findPreference(AppSettings.KEEP_FOREGROUND_SERVICE);
70 final var callIntegration = findPreference(AppSettings.CALL_INTEGRATION);
71 if (messageNotificationSettings == null
72 || fullscreenNotification == null
73 || notificationRingtone == null
74 || notificationHeadsUp == null
75 || notificationVibrate == null
76 || notificationLed == null
77 || foregroundService == null
78 || callIntegration == null) {
79 throw new IllegalStateException("The preference resource file is missing preferences");
80 }
81 if (Compatibility.runsTwentySix()) {
82 notificationRingtone.setVisible(false);
83 notificationHeadsUp.setVisible(false);
84 notificationVibrate.setVisible(false);
85 notificationLed.setVisible(false);
86 foregroundService.setVisible(false);
87 } else {
88 messageNotificationSettings.setVisible(false);
89 }
90 fullscreenNotification.setOnPreferenceClickListener(this::manageAppUseFullScreen);
91 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
92 || requireContext()
93 .getSystemService(NotificationManager.class)
94 .canUseFullScreenIntent()) {
95 fullscreenNotification.setVisible(false);
96 }
97
98 final var sharedPreferences = getPreferenceManager().getSharedPreferences();
99 if (!sharedPreferences.getBoolean("notifications_from_strangers", true) && sharedPreferences.getString("chat_requests", null) == null) {
100 chatRequests.setValue("strangers");
101 }
102
103 callIntegration.setVisible(CallIntegration.selfManagedAvailable(requireContext()));
104 }
105
106 @Override
107 public void onResume() {
108 super.onResume();
109 final var fullscreenNotification = findPreference("fullscreen_notification");
110 if (fullscreenNotification == null) {
111 return;
112 }
113 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
114 || requireContext()
115 .getSystemService(NotificationManager.class)
116 .canUseFullScreenIntent()) {
117 fullscreenNotification.setVisible(false);
118 }
119 }
120
121 private boolean manageAppUseFullScreen(final Preference preference) {
122 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
123 return false;
124 }
125 final var intent = new Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT);
126 intent.setData(Uri.parse(String.format("package:%s", requireContext().getPackageName())));
127 try {
128 startActivity(intent);
129 } catch (final ActivityNotFoundException e) {
130 Toast.makeText(requireContext(), R.string.unsupported_operation, Toast.LENGTH_SHORT)
131 .show();
132 return false;
133 }
134 return true;
135 }
136
137 @Override
138 protected void onSharedPreferenceChanged(@NonNull String key) {
139 super.onSharedPreferenceChanged(key);
140 if (key.equals(AppSettings.KEEP_FOREGROUND_SERVICE)) {
141 requireService().toggleForegroundService();
142 }
143 }
144
145 @Override
146 public void onStart() {
147 super.onStart();
148 requireActivity().setTitle(R.string.notifications);
149 }
150
151 @Override
152 public void onBackendConnected() {
153 boolean diallerIntegrationPossible = false;
154
155 if (Build.VERSION.SDK_INT >= 23) {
156 diallerIntegrationPossible = requireService().getAccounts().stream().anyMatch(a -> a.getGateways("pstn").size() > 0);
157 }
158 if (!diallerIntegrationPossible) {
159 final var pref = findPreference("dialler_integration_incoming");
160 pref.setVisible(false);
161 }
162 }
163
164 @Override
165 public boolean onPreferenceTreeClick(final Preference preference) {
166 final var key = preference.getKey();
167 if (AppSettings.RINGTONE.equals(key)) {
168 pickRingtone();
169 return true;
170 }
171 if (AppSettings.NOTIFICATION_RINGTONE.equals(key)) {
172 pickNotificationTone();
173 return true;
174 }
175 return super.onPreferenceTreeClick(preference);
176 }
177
178 private void pickNotificationTone() {
179 final Uri uri = appSettings().getNotificationTone();
180 Log.i(Config.LOGTAG, "current notification tone: " + uri);
181 this.pickNotificationToneLauncher.launch(uri);
182 }
183
184 private void pickRingtone() {
185 final Optional<Uri> channelRingtone;
186 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
187 channelRingtone =
188 NotificationService.getCurrentIncomingCallChannel(requireContext())
189 .transform(channel -> PickRingtone.nullToNone(channel.getSound()));
190 } else {
191 channelRingtone = Optional.absent();
192 }
193 final Uri uri;
194 if (channelRingtone.isPresent()) {
195 uri = channelRingtone.get();
196 Log.d(Config.LOGTAG, "ringtone came from channel");
197 } else {
198 uri = appSettings().getRingtone();
199 }
200 Log.i(Config.LOGTAG, "current ringtone: " + uri);
201 try {
202 this.pickRingtoneLauncher.launch(uri);
203 } catch (final ActivityNotFoundException e) {
204 Toast.makeText(requireActivity(), R.string.no_application_found, Toast.LENGTH_LONG)
205 .show();
206 }
207 }
208
209 private AppSettings appSettings() {
210 return new AppSettings(requireContext());
211 }
212}