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