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