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