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