NotificationsSettingsFragment.java

  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}