SettingsActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.FragmentManager;
  4import android.content.Context;
  5import android.content.DialogInterface;
  6import android.content.Intent;
  7import android.content.SharedPreferences;
  8import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
  9import android.content.pm.PackageManager;
 10import android.net.Uri;
 11import android.os.Build;
 12import android.os.Bundle;
 13import android.os.storage.StorageManager;
 14import android.preference.CheckBoxPreference;
 15import android.preference.ListPreference;
 16import android.preference.Preference;
 17import android.preference.PreferenceCategory;
 18import android.preference.PreferenceManager;
 19import android.preference.PreferenceScreen;
 20import android.provider.MediaStore;
 21import android.util.Log;
 22import android.widget.Toast;
 23
 24import androidx.annotation.NonNull;
 25import androidx.appcompat.app.AlertDialog;
 26import androidx.core.content.ContextCompat;
 27
 28import com.google.common.base.Strings;
 29import com.google.common.collect.ImmutableList;
 30import com.google.common.collect.Lists;
 31
 32import java.io.File;
 33import java.security.KeyStoreException;
 34import java.util.ArrayList;
 35import java.util.Arrays;
 36import java.util.Collections;
 37import java.util.List;
 38
 39import eu.siacs.conversations.Config;
 40import eu.siacs.conversations.R;
 41import eu.siacs.conversations.crypto.OmemoSetting;
 42import eu.siacs.conversations.entities.Account;
 43import eu.siacs.conversations.entities.Contact;
 44import eu.siacs.conversations.persistance.FileBackend;
 45import eu.siacs.conversations.services.ExportBackupService;
 46import eu.siacs.conversations.services.MemorizingTrustManager;
 47import eu.siacs.conversations.services.QuickConversationsService;
 48import eu.siacs.conversations.services.UnifiedPushDistributor;
 49import eu.siacs.conversations.ui.util.SettingsUtils;
 50import eu.siacs.conversations.ui.util.StyledAttributes;
 51import eu.siacs.conversations.utils.GeoHelper;
 52import eu.siacs.conversations.utils.TimeFrameUtils;
 53import eu.siacs.conversations.xmpp.Jid;
 54
 55public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener {
 56
 57    public static final String KEEP_FOREGROUND_SERVICE = "enable_foreground_service";
 58    public static final String AWAY_WHEN_SCREEN_IS_OFF = "away_when_screen_off";
 59    public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent";
 60    public static final String DND_ON_SILENT_MODE = "dnd_on_silent_mode";
 61    public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence";
 62    public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
 63    public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion";
 64    public static final String BROADCAST_LAST_ACTIVITY = "last_activity";
 65    public static final String THEME = "theme";
 66    public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
 67    public static final String OMEMO_SETTING = "omemo";
 68    public static final String PREVENT_SCREENSHOTS = "prevent_screenshots";
 69
 70    public static final int REQUEST_CREATE_BACKUP = 0xbf8701;
 71
 72    private SettingsFragment mSettingsFragment;
 73
 74    @Override
 75    protected void onCreate(Bundle savedInstanceState) {
 76        super.onCreate(savedInstanceState);
 77        setContentView(R.layout.activity_settings);
 78        FragmentManager fm = getFragmentManager();
 79        mSettingsFragment = (SettingsFragment) fm.findFragmentById(R.id.settings_content);
 80        if (mSettingsFragment == null
 81                || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
 82            mSettingsFragment = new SettingsFragment();
 83            fm.beginTransaction().replace(R.id.settings_content, mSettingsFragment).commit();
 84        }
 85        mSettingsFragment.setActivityIntent(getIntent());
 86        this.mTheme = findTheme();
 87        setTheme(this.mTheme);
 88        getWindow()
 89                .getDecorView()
 90                .setBackgroundColor(
 91                        StyledAttributes.getColor(this, R.attr.color_background_primary));
 92        setSupportActionBar(findViewById(R.id.toolbar));
 93        configureActionBar(getSupportActionBar());
 94    }
 95
 96    @Override
 97    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 98        SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
 99        p.edit().putString("sticker_directory", data.getData().toString()).commit();
100    }
101
102    @Override
103    void onBackendConnected() {
104        boolean diallerIntegrationPossible = false;
105
106        if (Build.VERSION.SDK_INT >= 23) {
107            outer:
108            for (Account account : xmppConnectionService.getAccounts()) {
109                for (Contact contact : account.getRoster().getContacts()) {
110                    if (contact.getPresences().anyIdentity("gateway", "pstn")) {
111                        diallerIntegrationPossible = true;
112                        break outer;
113                    }
114                }
115            }
116        }
117        if (!diallerIntegrationPossible) {
118            PreferenceCategory cat = (PreferenceCategory) mSettingsFragment.findPreference("notification_category");
119            Preference pref = mSettingsFragment.findPreference("dialler_integration_incoming");
120            if (cat != null && pref != null) cat.removePreference(pref);
121        }
122        final Preference accountPreference =
123                mSettingsFragment.findPreference(UnifiedPushDistributor.PREFERENCE_ACCOUNT);
124        reconfigureUpAccountPreference(accountPreference);
125    }
126
127    private void reconfigureUpAccountPreference(final Preference preference) {
128        final ListPreference listPreference;
129        if (preference instanceof ListPreference) {
130            listPreference = (ListPreference) preference;
131        } else {
132            return;
133        }
134        final List<CharSequence> accounts =
135                ImmutableList.copyOf(
136                        Lists.transform(
137                                xmppConnectionService.getAccounts(),
138                                a -> a.getJid().asBareJid().toEscapedString()));
139        final ImmutableList.Builder<CharSequence> entries = new ImmutableList.Builder<>();
140        final ImmutableList.Builder<CharSequence> entryValues = new ImmutableList.Builder<>();
141        entries.add(getString(R.string.no_account_deactivated));
142        entryValues.add("none");
143        entries.addAll(accounts);
144        entryValues.addAll(accounts);
145        listPreference.setEntries(entries.build().toArray(new CharSequence[0]));
146        listPreference.setEntryValues(entryValues.build().toArray(new CharSequence[0]));
147        if (!accounts.contains(listPreference.getValue())) {
148            listPreference.setValue("none");
149        }
150    }
151
152    @Override
153    public void onStart() {
154        super.onStart();
155        PreferenceManager.getDefaultSharedPreferences(this)
156                .registerOnSharedPreferenceChangeListener(this);
157
158        changeOmemoSettingSummary();
159
160        if (QuickConversationsService.isQuicksy()
161                || Strings.isNullOrEmpty(Config.CHANNEL_DISCOVERY)) {
162            final PreferenceCategory groupChats =
163                    (PreferenceCategory) mSettingsFragment.findPreference("group_chats");
164            final Preference channelDiscoveryMethod =
165                    mSettingsFragment.findPreference("channel_discovery_method");
166            if (groupChats != null && channelDiscoveryMethod != null) {
167                groupChats.removePreference(channelDiscoveryMethod);
168            }
169        }
170
171        if (QuickConversationsService.isQuicksy()) {
172            final PreferenceCategory connectionOptions =
173                    (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
174            PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
175            if (connectionOptions != null) {
176                expert.removePreference(connectionOptions);
177            }
178        }
179
180        PreferenceScreen mainPreferenceScreen =
181                (PreferenceScreen) mSettingsFragment.findPreference("main_screen");
182
183        PreferenceCategory attachmentsCategory =
184                (PreferenceCategory) mSettingsFragment.findPreference("attachments");
185        CheckBoxPreference locationPlugin =
186                (CheckBoxPreference) mSettingsFragment.findPreference("use_share_location_plugin");
187        if (attachmentsCategory != null && locationPlugin != null) {
188            if (!GeoHelper.isLocationPluginInstalled(this)) {
189                attachmentsCategory.removePreference(locationPlugin);
190            }
191        }
192
193        // this feature is only available on Huawei Android 6.
194        PreferenceScreen huaweiPreferenceScreen =
195                (PreferenceScreen) mSettingsFragment.findPreference("huawei");
196        if (huaweiPreferenceScreen != null) {
197            Intent intent = huaweiPreferenceScreen.getIntent();
198            // remove when Api version is above M (Version 6.0) or if the intent is not callable
199            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M || !isCallable(intent)) {
200                PreferenceCategory generalCategory =
201                        (PreferenceCategory) mSettingsFragment.findPreference("general");
202                generalCategory.removePreference(huaweiPreferenceScreen);
203                if (generalCategory.getPreferenceCount() == 0) {
204                    if (mainPreferenceScreen != null) {
205                        mainPreferenceScreen.removePreference(generalCategory);
206                    }
207                }
208            }
209        }
210
211        ListPreference automaticMessageDeletionList =
212                (ListPreference) mSettingsFragment.findPreference(AUTOMATIC_MESSAGE_DELETION);
213        if (automaticMessageDeletionList != null) {
214            final int[] choices =
215                    getResources().getIntArray(R.array.automatic_message_deletion_values);
216            CharSequence[] entries = new CharSequence[choices.length];
217            CharSequence[] entryValues = new CharSequence[choices.length];
218            for (int i = 0; i < choices.length; ++i) {
219                entryValues[i] = String.valueOf(choices[i]);
220                if (choices[i] == 0) {
221                    entries[i] = getString(R.string.never);
222                } else {
223                    entries[i] = TimeFrameUtils.resolve(this, 1000L * choices[i]);
224                }
225            }
226            automaticMessageDeletionList.setEntries(entries);
227            automaticMessageDeletionList.setEntryValues(entryValues);
228        }
229
230        boolean removeLocation =
231                new Intent("eu.siacs.conversations.location.request")
232                                .resolveActivity(getPackageManager())
233                        == null;
234        boolean removeVoice =
235                new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
236                                .resolveActivity(getPackageManager())
237                        == null;
238
239        ListPreference quickAction =
240                (ListPreference) mSettingsFragment.findPreference("quick_action");
241        if (quickAction != null && (removeLocation || removeVoice)) {
242            ArrayList<CharSequence> entries =
243                    new ArrayList<>(Arrays.asList(quickAction.getEntries()));
244            ArrayList<CharSequence> entryValues =
245                    new ArrayList<>(Arrays.asList(quickAction.getEntryValues()));
246            int index = entryValues.indexOf("location");
247            if (index > 0 && removeLocation) {
248                entries.remove(index);
249                entryValues.remove(index);
250            }
251            index = entryValues.indexOf("voice");
252            if (index > 0 && removeVoice) {
253                entries.remove(index);
254                entryValues.remove(index);
255            }
256            quickAction.setEntries(entries.toArray(new CharSequence[entries.size()]));
257            quickAction.setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()]));
258        }
259
260        final Preference removeCertsPreference =
261                mSettingsFragment.findPreference("remove_trusted_certificates");
262        if (removeCertsPreference != null) {
263            removeCertsPreference.setOnPreferenceClickListener(
264                    preference -> {
265                        final MemorizingTrustManager mtm =
266                                xmppConnectionService.getMemorizingTrustManager();
267                        final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
268                        if (aliases.size() == 0) {
269                            displayToast(getString(R.string.toast_no_trusted_certs));
270                            return true;
271                        }
272                        final ArrayList<Integer> selectedItems = new ArrayList<>();
273                        final AlertDialog.Builder dialogBuilder =
274                                new AlertDialog.Builder(SettingsActivity.this);
275                        dialogBuilder.setTitle(
276                                getResources().getString(R.string.dialog_manage_certs_title));
277                        dialogBuilder.setMultiChoiceItems(
278                                aliases.toArray(new CharSequence[aliases.size()]),
279                                null,
280                                (dialog, indexSelected, isChecked) -> {
281                                    if (isChecked) {
282                                        selectedItems.add(indexSelected);
283                                    } else if (selectedItems.contains(indexSelected)) {
284                                        selectedItems.remove(Integer.valueOf(indexSelected));
285                                    }
286                                    ((AlertDialog) dialog)
287                                            .getButton(DialogInterface.BUTTON_POSITIVE)
288                                            .setEnabled(selectedItems.size() > 0);
289                                });
290
291                        dialogBuilder.setPositiveButton(
292                                getResources()
293                                        .getString(R.string.dialog_manage_certs_positivebutton),
294                                (dialog, which) -> {
295                                    int count = selectedItems.size();
296                                    if (count > 0) {
297                                        for (int i = 0; i < count; i++) {
298                                            try {
299                                                Integer item =
300                                                        Integer.valueOf(
301                                                                selectedItems.get(i).toString());
302                                                String alias = aliases.get(item);
303                                                mtm.deleteCertificate(alias);
304                                            } catch (KeyStoreException e) {
305                                                e.printStackTrace();
306                                                displayToast("Error: " + e.getLocalizedMessage());
307                                            }
308                                        }
309                                        if (xmppConnectionServiceBound) {
310                                            reconnectAccounts();
311                                        }
312                                        displayToast(
313                                                getResources()
314                                                        .getQuantityString(
315                                                                R.plurals.toast_delete_certificates,
316                                                                count,
317                                                                count));
318                                    }
319                                });
320                        dialogBuilder.setNegativeButton(
321                                getResources()
322                                        .getString(R.string.dialog_manage_certs_negativebutton),
323                                null);
324                        AlertDialog removeCertsDialog = dialogBuilder.create();
325                        removeCertsDialog.show();
326                        removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
327                        return true;
328                    });
329        }
330
331        final Preference createBackupPreference = mSettingsFragment.findPreference("create_backup");
332        if (createBackupPreference != null) {
333            createBackupPreference.setSummary(
334                    getString(
335                            R.string.pref_create_backup_summary,
336                            FileBackend.getBackupDirectory(this).getAbsolutePath()));
337            createBackupPreference.setOnPreferenceClickListener(
338                    preference -> {
339                        if (hasStoragePermission(REQUEST_CREATE_BACKUP)) {
340                            createBackup();
341                        }
342                        return true;
343                    });
344        }
345
346        if (Config.ONLY_INTERNAL_STORAGE) {
347            final Preference cleanCachePreference = mSettingsFragment.findPreference("clean_cache");
348            if (cleanCachePreference != null) {
349                cleanCachePreference.setOnPreferenceClickListener(preference -> cleanCache());
350            }
351
352            final Preference cleanPrivateStoragePreference =
353                    mSettingsFragment.findPreference("clean_private_storage");
354            if (cleanPrivateStoragePreference != null) {
355                cleanPrivateStoragePreference.setOnPreferenceClickListener(
356                        preference -> cleanPrivateStorage());
357            }
358        }
359
360        final Preference deleteOmemoPreference =
361                mSettingsFragment.findPreference("delete_omemo_identities");
362        if (deleteOmemoPreference != null) {
363            deleteOmemoPreference.setOnPreferenceClickListener(
364                    preference -> deleteOmemoIdentities());
365        }
366        if (Config.omemoOnly()) {
367            final PreferenceCategory privacyCategory =
368                    (PreferenceCategory) mSettingsFragment.findPreference("privacy");
369            final Preference omemoPreference =mSettingsFragment.findPreference(OMEMO_SETTING);
370            if (omemoPreference != null) {
371                privacyCategory.removePreference(omemoPreference);
372            }
373        }
374
375        final Preference stickerDir = mSettingsFragment.findPreference("sticker_directory");
376        stickerDir.setOnPreferenceClickListener((p) -> {
377            Intent intent = ((StorageManager) getSystemService(Context.STORAGE_SERVICE)).getPrimaryStorageVolume().createOpenDocumentTreeIntent();
378            startActivityForResult(Intent.createChooser(intent, "Choose sticker location"), 0);
379            return true;
380        });
381    }
382
383    private void changeOmemoSettingSummary() {
384        final ListPreference omemoPreference =
385                (ListPreference) mSettingsFragment.findPreference(OMEMO_SETTING);
386        if (omemoPreference == null) {
387            return;
388        }
389        final String value = omemoPreference.getValue();
390        switch (value) {
391            case "always":
392                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_always);
393                break;
394            case "default_on":
395                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_on);
396                break;
397            case "default_off":
398                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_off);
399                break;
400        }
401    }
402
403    private boolean isCallable(final Intent i) {
404        return i != null
405                && getPackageManager()
406                                .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY)
407                                .size()
408                        > 0;
409    }
410
411    private boolean cleanCache() {
412        Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
413        intent.setData(Uri.parse("package:" + getPackageName()));
414        startActivity(intent);
415        return true;
416    }
417
418    private boolean cleanPrivateStorage() {
419        for (String type : Arrays.asList("Images", "Videos", "Files", "Recordings")) {
420            cleanPrivateFiles(type);
421        }
422        return true;
423    }
424
425    private void cleanPrivateFiles(final String type) {
426        try {
427            File dir = new File(getFilesDir().getAbsolutePath(), "/" + type + "/");
428            File[] array = dir.listFiles();
429            if (array != null) {
430                for (int b = 0; b < array.length; b++) {
431                    String name = array[b].getName().toLowerCase();
432                    if (name.equals(".nomedia")) {
433                        continue;
434                    }
435                    if (array[b].isFile()) {
436                        array[b].delete();
437                    }
438                }
439            }
440        } catch (Throwable e) {
441            Log.e("CleanCache", e.toString());
442        }
443    }
444
445    private boolean deleteOmemoIdentities() {
446        AlertDialog.Builder builder = new AlertDialog.Builder(this);
447        builder.setTitle(R.string.pref_delete_omemo_identities);
448        final List<CharSequence> accounts = new ArrayList<>();
449        for (Account account : xmppConnectionService.getAccounts()) {
450            if (account.isEnabled()) {
451                accounts.add(account.getJid().asBareJid().toString());
452            }
453        }
454        final boolean[] checkedItems = new boolean[accounts.size()];
455        builder.setMultiChoiceItems(
456                accounts.toArray(new CharSequence[accounts.size()]),
457                checkedItems,
458                (dialog, which, isChecked) -> {
459                    checkedItems[which] = isChecked;
460                    final AlertDialog alertDialog = (AlertDialog) dialog;
461                    for (boolean item : checkedItems) {
462                        if (item) {
463                            alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
464                            return;
465                        }
466                    }
467                    alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
468                });
469        builder.setNegativeButton(R.string.cancel, null);
470        builder.setPositiveButton(
471                R.string.delete_selected_keys,
472                (dialog, which) -> {
473                    for (int i = 0; i < checkedItems.length; ++i) {
474                        if (checkedItems[i]) {
475                            try {
476                                Jid jid = Jid.of(accounts.get(i).toString());
477                                Account account = xmppConnectionService.findAccountByJid(jid);
478                                if (account != null) {
479                                    account.getAxolotlService().regenerateKeys(true);
480                                }
481                            } catch (IllegalArgumentException e) {
482                                //
483                            }
484                        }
485                    }
486                });
487        AlertDialog dialog = builder.create();
488        dialog.show();
489        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
490        return true;
491    }
492
493    @Override
494    public void onStop() {
495        super.onStop();
496        PreferenceManager.getDefaultSharedPreferences(this)
497                .unregisterOnSharedPreferenceChangeListener(this);
498    }
499
500    @Override
501    public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
502        final List<String> resendPresence =
503                Arrays.asList(
504                        "confirm_messages",
505                        DND_ON_SILENT_MODE,
506                        AWAY_WHEN_SCREEN_IS_OFF,
507                        "allow_message_correction",
508                        TREAT_VIBRATE_AS_SILENT,
509                        MANUALLY_CHANGE_PRESENCE,
510                        BROADCAST_LAST_ACTIVITY);
511        if (name.equals(OMEMO_SETTING)) {
512            OmemoSetting.load(this, preferences);
513            changeOmemoSettingSummary();
514        } else if (name.equals(KEEP_FOREGROUND_SERVICE)) {
515            xmppConnectionService.toggleForegroundService();
516        } else if (resendPresence.contains(name)) {
517            if (xmppConnectionServiceBound) {
518                if (name.equals(AWAY_WHEN_SCREEN_IS_OFF) || name.equals(MANUALLY_CHANGE_PRESENCE)) {
519                    xmppConnectionService.toggleScreenEventReceiver();
520                }
521                xmppConnectionService.refreshAllPresences();
522            }
523        } else if (name.equals("dont_trust_system_cas")) {
524            xmppConnectionService.updateMemorizingTrustmanager();
525            reconnectAccounts();
526        } else if (name.equals("use_tor")) {
527            if (preferences.getBoolean(name, false)) {
528                displayToast(getString(R.string.audio_video_disabled_tor));
529            }
530            reconnectAccounts();
531            xmppConnectionService.reinitializeMuclumbusService();
532        } else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) {
533            xmppConnectionService.expireOldMessages(true);
534        } else if (name.equals(THEME)) {
535            final int theme = findTheme();
536            if (this.mTheme != theme) {
537                xmppConnectionService.setTheme(theme);
538                recreate();
539            }
540        } else if (name.equals(PREVENT_SCREENSHOTS)) {
541            SettingsUtils.applyScreenshotPreventionSetting(this);
542        } else if (UnifiedPushDistributor.PREFERENCES.contains(name)) {
543            if (xmppConnectionService.reconfigurePushDistributor()) {
544                xmppConnectionService.renewUnifiedPushEndpoints();
545            }
546        }
547    }
548
549    @Override
550    public void onResume() {
551        super.onResume();
552        SettingsUtils.applyScreenshotPreventionSetting(this);
553    }
554
555    @Override
556    public void onRequestPermissionsResult(
557            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
558        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
559        if (grantResults.length > 0)
560            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
561                if (requestCode == REQUEST_CREATE_BACKUP) {
562                    createBackup();
563                }
564            } else {
565                Toast.makeText(
566                                this,
567                                getString(
568                                        R.string.no_storage_permission,
569                                        getString(R.string.app_name)),
570                                Toast.LENGTH_SHORT)
571                        .show();
572            }
573    }
574
575    private void createBackup() {
576        new AlertDialog.Builder(this)
577            .setTitle("Create Backup")
578            .setMessage("Export extra Cheogram-only data (backup will not import into other apps then)?")
579            .setPositiveButton(R.string.yes, (dialog, whichButton) -> {
580                createBackup(true);
581            })
582            .setNegativeButton(R.string.no, (dialog, whichButton) -> {
583                createBackup(false);
584            }).show();
585    }
586
587    private void createBackup(boolean withCheogramDb) {
588        Intent intent = new Intent(this, ExportBackupService.class);
589        intent.putExtra("cheogram_db", withCheogramDb);
590        ContextCompat.startForegroundService(this, intent);
591        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
592        builder.setMessage(R.string.backup_started_message);
593        builder.setPositiveButton(R.string.ok, null);
594        builder.create().show();
595    }
596
597    private void displayToast(final String msg) {
598        runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show());
599    }
600
601    private void reconnectAccounts() {
602        for (Account account : xmppConnectionService.getAccounts()) {
603            if (account.isEnabled()) {
604                xmppConnectionService.reconnectAccountInBackground(account);
605            }
606        }
607    }
608
609    public void refreshUiReal() {
610        // nothing to do. This Activity doesn't implement any listeners
611    }
612}