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