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;
 13import androidx.activity.result.ActivityResultLauncher;
 14import androidx.annotation.NonNull;
 15import androidx.annotation.Nullable;
 16import androidx.preference.Preference;
 17import androidx.preference.ListPreference;
 18
 19import com.google.common.base.Optional;
 20import eu.siacs.conversations.AppSettings;
 21import eu.siacs.conversations.Config;
 22import eu.siacs.conversations.R;
 23import eu.siacs.conversations.services.CallIntegration;
 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 chatRequests = (ListPreference) findPreference("chat_requests");
 69        final var foregroundService = findPreference(AppSettings.KEEP_FOREGROUND_SERVICE);
 70        final var callIntegration = findPreference(AppSettings.CALL_INTEGRATION);
 71        if (messageNotificationSettings == null
 72                || fullscreenNotification == null
 73                || notificationRingtone == null
 74                || notificationHeadsUp == null
 75                || notificationVibrate == null
 76                || notificationLed == null
 77                || foregroundService == null
 78                || callIntegration == null) {
 79            throw new IllegalStateException("The preference resource file is missing preferences");
 80        }
 81        if (Compatibility.runsTwentySix()) {
 82            notificationRingtone.setVisible(false);
 83            notificationHeadsUp.setVisible(false);
 84            notificationVibrate.setVisible(false);
 85            notificationLed.setVisible(false);
 86            foregroundService.setVisible(false);
 87        } else {
 88            messageNotificationSettings.setVisible(false);
 89        }
 90        fullscreenNotification.setOnPreferenceClickListener(this::manageAppUseFullScreen);
 91        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 92                || requireContext()
 93                        .getSystemService(NotificationManager.class)
 94                        .canUseFullScreenIntent()) {
 95            fullscreenNotification.setVisible(false);
 96        }
 97
 98        final var sharedPreferences = getPreferenceManager().getSharedPreferences();
 99        if (!sharedPreferences.getBoolean("notifications_from_strangers", true) && sharedPreferences.getString("chat_requests", null) == null) {
100            chatRequests.setValue("strangers");
101        }
102
103        callIntegration.setVisible(CallIntegration.selfManagedAvailable(requireContext()));
104    }
105
106    @Override
107    public void onResume() {
108        super.onResume();
109        final var fullscreenNotification = findPreference("fullscreen_notification");
110        if (fullscreenNotification == null) {
111            return;
112        }
113        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
114                || requireContext()
115                        .getSystemService(NotificationManager.class)
116                        .canUseFullScreenIntent()) {
117            fullscreenNotification.setVisible(false);
118        }
119    }
120
121    private boolean manageAppUseFullScreen(final Preference preference) {
122        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
123            return false;
124        }
125        final var intent = new Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT);
126        intent.setData(Uri.parse(String.format("package:%s", requireContext().getPackageName())));
127        try {
128            startActivity(intent);
129        } catch (final ActivityNotFoundException e) {
130            Toast.makeText(requireContext(), R.string.unsupported_operation, Toast.LENGTH_SHORT)
131                    .show();
132            return false;
133        }
134        return true;
135    }
136
137    @Override
138    protected void onSharedPreferenceChanged(@NonNull String key) {
139        super.onSharedPreferenceChanged(key);
140        if (key.equals(AppSettings.KEEP_FOREGROUND_SERVICE)) {
141            requireService().toggleForegroundService();
142        }
143    }
144
145    @Override
146    public void onStart() {
147        super.onStart();
148        requireActivity().setTitle(R.string.notifications);
149    }
150
151    @Override
152    public void onBackendConnected() {
153        boolean diallerIntegrationPossible = false;
154
155        if (Build.VERSION.SDK_INT >= 23) {
156            diallerIntegrationPossible = requireService().getAccounts().stream().anyMatch(a -> a.getGateways("pstn").size() > 0);
157        }
158        if (!diallerIntegrationPossible) {
159            final var pref = findPreference("dialler_integration_incoming");
160            pref.setVisible(false);
161        }
162    }
163
164    @Override
165    public boolean onPreferenceTreeClick(final Preference preference) {
166        final var key = preference.getKey();
167        if (AppSettings.RINGTONE.equals(key)) {
168            pickRingtone();
169            return true;
170        }
171        if (AppSettings.NOTIFICATION_RINGTONE.equals(key)) {
172            pickNotificationTone();
173            return true;
174        }
175        return super.onPreferenceTreeClick(preference);
176    }
177
178    private void pickNotificationTone() {
179        final Uri uri = appSettings().getNotificationTone();
180        Log.i(Config.LOGTAG, "current notification tone: " + uri);
181        this.pickNotificationToneLauncher.launch(uri);
182    }
183
184    private void pickRingtone() {
185        final Optional<Uri> channelRingtone;
186        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
187            channelRingtone =
188                    NotificationService.getCurrentIncomingCallChannel(requireContext())
189                            .transform(channel -> PickRingtone.nullToNone(channel.getSound()));
190        } else {
191            channelRingtone = Optional.absent();
192        }
193        final Uri uri;
194        if (channelRingtone.isPresent()) {
195            uri = channelRingtone.get();
196            Log.d(Config.LOGTAG, "ringtone came from channel");
197        } else {
198            uri = appSettings().getRingtone();
199        }
200        Log.i(Config.LOGTAG, "current ringtone: " + uri);
201        try {
202            this.pickRingtoneLauncher.launch(uri);
203        } catch (final ActivityNotFoundException e) {
204            Toast.makeText(requireActivity(), R.string.no_application_found, Toast.LENGTH_LONG)
205                    .show();
206        }
207    }
208
209    private AppSettings appSettings() {
210        return new AppSettings(requireContext());
211    }
212}