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;
 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}