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.unsupported_operation, 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 void onBackendConnected() {
143 boolean diallerIntegrationPossible = false;
144
145 if (Build.VERSION.SDK_INT >= 23) {
146 outer:
147 for (final var account : requireService().getAccounts()) {
148 for (final var contact : account.getRoster().getContacts()) {
149 if (contact.getPresences().anyIdentity("gateway", "pstn")) {
150 diallerIntegrationPossible = true;
151 break outer;
152 }
153 }
154 }
155 }
156 if (!diallerIntegrationPossible) {
157 final var pref = findPreference("dialler_integration_incoming");
158 pref.setVisible(false);
159 }
160 }
161
162 @Override
163 public boolean onPreferenceTreeClick(final Preference preference) {
164 final var key = preference.getKey();
165 if (AppSettings.RINGTONE.equals(key)) {
166 pickRingtone();
167 return true;
168 }
169 if (AppSettings.NOTIFICATION_RINGTONE.equals(key)) {
170 pickNotificationTone();
171 return true;
172 }
173 return super.onPreferenceTreeClick(preference);
174 }
175
176 private void pickNotificationTone() {
177 final Uri uri = appSettings().getNotificationTone();
178 Log.i(Config.LOGTAG, "current notification tone: " + uri);
179 this.pickNotificationToneLauncher.launch(uri);
180 }
181
182 private void pickRingtone() {
183 final Optional<Uri> channelRingtone;
184 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
185 channelRingtone =
186 NotificationService.getCurrentIncomingCallChannel(requireContext())
187 .transform(channel -> PickRingtone.nullToNone(channel.getSound()));
188 } else {
189 channelRingtone = Optional.absent();
190 }
191 final Uri uri;
192 if (channelRingtone.isPresent()) {
193 uri = channelRingtone.get();
194 Log.d(Config.LOGTAG, "ringtone came from channel");
195 } else {
196 uri = appSettings().getRingtone();
197 }
198 Log.i(Config.LOGTAG, "current ringtone: " + uri);
199 this.pickRingtoneLauncher.launch(uri);
200 }
201
202 private AppSettings appSettings() {
203 return new AppSettings(requireContext());
204 }
205}