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