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                || Strings.isNullOrEmpty(Config.CHANNEL_DISCOVERY)) {
177            final PreferenceCategory groupChats =
178                    (PreferenceCategory) mSettingsFragment.findPreference("group_chats");
179            final Preference channelDiscoveryMethod =
180                    mSettingsFragment.findPreference("channel_discovery_method");
181            if (groupChats != null && channelDiscoveryMethod != null) {
182                groupChats.removePreference(channelDiscoveryMethod);
183            }
184        }
185
186        if (QuickConversationsService.isQuicksy()) {
187            final PreferenceCategory connectionOptions =
188                    (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
189            PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
190            if (connectionOptions != null) {
191                expert.removePreference(connectionOptions);
192            }
193        }
194
195        PreferenceScreen mainPreferenceScreen =
196                (PreferenceScreen) mSettingsFragment.findPreference("main_screen");
197
198        PreferenceCategory attachmentsCategory =
199                (PreferenceCategory) mSettingsFragment.findPreference("attachments");
200        CheckBoxPreference locationPlugin =
201                (CheckBoxPreference) mSettingsFragment.findPreference("use_share_location_plugin");
202        if (attachmentsCategory != null && locationPlugin != null) {
203            if (!GeoHelper.isLocationPluginInstalled(this)) {
204                attachmentsCategory.removePreference(locationPlugin);
205            }
206        }
207
208        // this feature is only available on Huawei Android 6.
209        PreferenceScreen huaweiPreferenceScreen =
210                (PreferenceScreen) mSettingsFragment.findPreference("huawei");
211        if (huaweiPreferenceScreen != null) {
212            Intent intent = huaweiPreferenceScreen.getIntent();
213            // remove when Api version is above M (Version 6.0) or if the intent is not callable
214            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M || !isCallable(intent)) {
215                PreferenceCategory generalCategory =
216                        (PreferenceCategory) mSettingsFragment.findPreference("general");
217                generalCategory.removePreference(huaweiPreferenceScreen);
218                if (generalCategory.getPreferenceCount() == 0) {
219                    if (mainPreferenceScreen != null) {
220                        mainPreferenceScreen.removePreference(generalCategory);
221                    }
222                }
223            }
224        }
225
226        ListPreference automaticMessageDeletionList =
227                (ListPreference) mSettingsFragment.findPreference(AUTOMATIC_MESSAGE_DELETION);
228        if (automaticMessageDeletionList != null) {
229            final int[] choices =
230                    getResources().getIntArray(R.array.automatic_message_deletion_values);
231            CharSequence[] entries = new CharSequence[choices.length];
232            CharSequence[] entryValues = new CharSequence[choices.length];
233            for (int i = 0; i < choices.length; ++i) {
234                entryValues[i] = String.valueOf(choices[i]);
235                if (choices[i] == 0) {
236                    entries[i] = getString(R.string.never);
237                } else {
238                    entries[i] = TimeFrameUtils.resolve(this, 1000L * choices[i]);
239                }
240            }
241            automaticMessageDeletionList.setEntries(entries);
242            automaticMessageDeletionList.setEntryValues(entryValues);
243        }
244
245        boolean removeLocation =
246                new Intent("eu.siacs.conversations.location.request")
247                                .resolveActivity(getPackageManager())
248                        == null;
249        boolean removeVoice =
250                new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
251                                .resolveActivity(getPackageManager())
252                        == null;
253
254        ListPreference quickAction =
255                (ListPreference) mSettingsFragment.findPreference("quick_action");
256        if (quickAction != null && (removeLocation || removeVoice)) {
257            ArrayList<CharSequence> entries =
258                    new ArrayList<>(Arrays.asList(quickAction.getEntries()));
259            ArrayList<CharSequence> entryValues =
260                    new ArrayList<>(Arrays.asList(quickAction.getEntryValues()));
261            int index = entryValues.indexOf("location");
262            if (index > 0 && removeLocation) {
263                entries.remove(index);
264                entryValues.remove(index);
265            }
266            index = entryValues.indexOf("voice");
267            if (index > 0 && removeVoice) {
268                entries.remove(index);
269                entryValues.remove(index);
270            }
271            quickAction.setEntries(entries.toArray(new CharSequence[entries.size()]));
272            quickAction.setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()]));
273        }
274
275        final Preference removeCertsPreference =
276                mSettingsFragment.findPreference("remove_trusted_certificates");
277        if (removeCertsPreference != null) {
278            removeCertsPreference.setOnPreferenceClickListener(
279                    preference -> {
280                        final MemorizingTrustManager mtm =
281                                xmppConnectionService.getMemorizingTrustManager();
282                        final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
283                        if (aliases.size() == 0) {
284                            displayToast(getString(R.string.toast_no_trusted_certs));
285                            return true;
286                        }
287                        final ArrayList<Integer> selectedItems = new ArrayList<>();
288                        final AlertDialog.Builder dialogBuilder =
289                                new AlertDialog.Builder(SettingsActivity.this);
290                        dialogBuilder.setTitle(
291                                getResources().getString(R.string.dialog_manage_certs_title));
292                        dialogBuilder.setMultiChoiceItems(
293                                aliases.toArray(new CharSequence[aliases.size()]),
294                                null,
295                                (dialog, indexSelected, isChecked) -> {
296                                    if (isChecked) {
297                                        selectedItems.add(indexSelected);
298                                    } else if (selectedItems.contains(indexSelected)) {
299                                        selectedItems.remove(Integer.valueOf(indexSelected));
300                                    }
301                                    ((AlertDialog) dialog)
302                                            .getButton(DialogInterface.BUTTON_POSITIVE)
303                                            .setEnabled(selectedItems.size() > 0);
304                                });
305
306                        dialogBuilder.setPositiveButton(
307                                getResources()
308                                        .getString(R.string.dialog_manage_certs_positivebutton),
309                                (dialog, which) -> {
310                                    int count = selectedItems.size();
311                                    if (count > 0) {
312                                        for (int i = 0; i < count; i++) {
313                                            try {
314                                                Integer item =
315                                                        Integer.valueOf(
316                                                                selectedItems.get(i).toString());
317                                                String alias = aliases.get(item);
318                                                mtm.deleteCertificate(alias);
319                                            } catch (KeyStoreException e) {
320                                                e.printStackTrace();
321                                                displayToast("Error: " + e.getLocalizedMessage());
322                                            }
323                                        }
324                                        if (xmppConnectionServiceBound) {
325                                            reconnectAccounts();
326                                        }
327                                        displayToast(
328                                                getResources()
329                                                        .getQuantityString(
330                                                                R.plurals.toast_delete_certificates,
331                                                                count,
332                                                                count));
333                                    }
334                                });
335                        dialogBuilder.setNegativeButton(
336                                getResources()
337                                        .getString(R.string.dialog_manage_certs_negativebutton),
338                                null);
339                        AlertDialog removeCertsDialog = dialogBuilder.create();
340                        removeCertsDialog.show();
341                        removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
342                        return true;
343                    });
344        }
345
346        final Preference createBackupPreference = mSettingsFragment.findPreference("create_backup");
347        if (createBackupPreference != null) {
348            createBackupPreference.setSummary(
349                    getString(
350                            R.string.pref_create_backup_summary,
351                            FileBackend.getBackupDirectory(this).getAbsolutePath()));
352            createBackupPreference.setOnPreferenceClickListener(
353                    preference -> {
354                        if (hasStoragePermission(REQUEST_CREATE_BACKUP)) {
355                            createBackup();
356                        }
357                        return true;
358                    });
359        }
360
361        if (Config.ONLY_INTERNAL_STORAGE) {
362            final Preference cleanCachePreference = mSettingsFragment.findPreference("clean_cache");
363            if (cleanCachePreference != null) {
364                cleanCachePreference.setOnPreferenceClickListener(preference -> cleanCache());
365            }
366
367            final Preference cleanPrivateStoragePreference =
368                    mSettingsFragment.findPreference("clean_private_storage");
369            if (cleanPrivateStoragePreference != null) {
370                cleanPrivateStoragePreference.setOnPreferenceClickListener(
371                        preference -> cleanPrivateStorage());
372            }
373        }
374
375        final Preference deleteOmemoPreference =
376                mSettingsFragment.findPreference("delete_omemo_identities");
377        if (deleteOmemoPreference != null) {
378            deleteOmemoPreference.setOnPreferenceClickListener(
379                    preference -> deleteOmemoIdentities());
380        }
381        if (Config.omemoOnly()) {
382            final PreferenceCategory privacyCategory =
383                    (PreferenceCategory) mSettingsFragment.findPreference("privacy");
384            final Preference omemoPreference =mSettingsFragment.findPreference(OMEMO_SETTING);
385            if (omemoPreference != null) {
386                privacyCategory.removePreference(omemoPreference);
387            }
388        }
389
390        final Preference stickerDir = mSettingsFragment.findPreference("sticker_directory");
391        if (stickerDir != null) {
392            if (Build.VERSION.SDK_INT >= 24) {
393                stickerDir.setOnPreferenceClickListener((p) -> {
394                    Intent intent = ((StorageManager) getSystemService(Context.STORAGE_SERVICE)).getPrimaryStorageVolume().createOpenDocumentTreeIntent();
395                    startActivityForResult(Intent.createChooser(intent, "Choose sticker location"), 0);
396                    return true;
397                });
398            } else {
399                PreferenceCategory expertMedia = (PreferenceCategory) mSettingsFragment.findPreference("expert_media");
400                expertMedia.removePreference(stickerDir);
401            }
402        }
403
404        final Preference downloadDefaultStickers = mSettingsFragment.findPreference("download_default_stickers");
405        if (downloadDefaultStickers != null) {
406            downloadDefaultStickers.setOnPreferenceClickListener(
407                    preference -> {
408                        if (hasStoragePermission(REQUEST_DOWNLOAD_STICKERS)) {
409                            downloadStickers();
410                        }
411                        return true;
412                    });
413        }
414
415        final Preference clearBlockedMedia = mSettingsFragment.findPreference("clear_blocked_media");
416        if (clearBlockedMedia != null) {
417            clearBlockedMedia.setOnPreferenceClickListener((p) -> {
418                xmppConnectionService.clearBlockedMedia();
419                displayToast("Blocked media will be displayed again.");
420                return true;
421            });
422        }
423
424        final String theTheme = PreferenceManager.getDefaultSharedPreferences(this).getString(THEME, "");
425        if (Build.VERSION.SDK_INT < 30 || !theTheme.equals("custom")) {
426            final PreferenceCategory uiCategory = (PreferenceCategory) mSettingsFragment.findPreference("ui");
427            final Preference customTheme = mSettingsFragment.findPreference("custom_theme");
428            if (customTheme != null) uiCategory.removePreference(customTheme);
429        }
430    }
431
432    private void changeOmemoSettingSummary() {
433        final ListPreference omemoPreference =
434                (ListPreference) mSettingsFragment.findPreference(OMEMO_SETTING);
435        if (omemoPreference == null) {
436            return;
437        }
438        final String value = omemoPreference.getValue();
439        switch (value) {
440            case "always":
441                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_always);
442                break;
443            case "default_on":
444                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_on);
445                break;
446            case "default_off":
447                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_off);
448                break;
449        }
450    }
451
452    private boolean isCallable(final Intent i) {
453        return i != null
454                && getPackageManager()
455                                .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY)
456                                .size()
457                        > 0;
458    }
459
460    private boolean cleanCache() {
461        Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
462        intent.setData(Uri.parse("package:" + getPackageName()));
463        startActivity(intent);
464        return true;
465    }
466
467    private boolean cleanPrivateStorage() {
468        for (String type : Arrays.asList("Images", "Videos", "Files", "Recordings")) {
469            cleanPrivateFiles(type);
470        }
471        return true;
472    }
473
474    private void cleanPrivateFiles(final String type) {
475        try {
476            File dir = new File(getFilesDir().getAbsolutePath(), "/" + type + "/");
477            File[] array = dir.listFiles();
478            if (array != null) {
479                for (int b = 0; b < array.length; b++) {
480                    String name = array[b].getName().toLowerCase();
481                    if (name.equals(".nomedia")) {
482                        continue;
483                    }
484                    if (array[b].isFile()) {
485                        array[b].delete();
486                    }
487                }
488            }
489        } catch (Throwable e) {
490            Log.e("CleanCache", e.toString());
491        }
492    }
493
494    private boolean deleteOmemoIdentities() {
495        AlertDialog.Builder builder = new AlertDialog.Builder(this);
496        builder.setTitle(R.string.pref_delete_omemo_identities);
497        final List<CharSequence> accounts = new ArrayList<>();
498        for (Account account : xmppConnectionService.getAccounts()) {
499            if (account.isEnabled()) {
500                accounts.add(account.getJid().asBareJid().toString());
501            }
502        }
503        final boolean[] checkedItems = new boolean[accounts.size()];
504        builder.setMultiChoiceItems(
505                accounts.toArray(new CharSequence[accounts.size()]),
506                checkedItems,
507                (dialog, which, isChecked) -> {
508                    checkedItems[which] = isChecked;
509                    final AlertDialog alertDialog = (AlertDialog) dialog;
510                    for (boolean item : checkedItems) {
511                        if (item) {
512                            alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
513                            return;
514                        }
515                    }
516                    alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
517                });
518        builder.setNegativeButton(R.string.cancel, null);
519        builder.setPositiveButton(
520                R.string.delete_selected_keys,
521                (dialog, which) -> {
522                    for (int i = 0; i < checkedItems.length; ++i) {
523                        if (checkedItems[i]) {
524                            try {
525                                Jid jid = Jid.of(accounts.get(i).toString());
526                                Account account = xmppConnectionService.findAccountByJid(jid);
527                                if (account != null) {
528                                    account.getAxolotlService().regenerateKeys(true);
529                                }
530                            } catch (IllegalArgumentException e) {
531                                //
532                            }
533                        }
534                    }
535                });
536        AlertDialog dialog = builder.create();
537        dialog.show();
538        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
539        return true;
540    }
541
542    @Override
543    public void onStop() {
544        super.onStop();
545        PreferenceManager.getDefaultSharedPreferences(this)
546                .unregisterOnSharedPreferenceChangeListener(this);
547    }
548
549    @Override
550    public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
551        final List<String> resendPresence =
552                Arrays.asList(
553                        "confirm_messages",
554                        DND_ON_SILENT_MODE,
555                        AWAY_WHEN_SCREEN_IS_OFF,
556                        "allow_message_correction",
557                        TREAT_VIBRATE_AS_SILENT,
558                        MANUALLY_CHANGE_PRESENCE,
559                        BROADCAST_LAST_ACTIVITY);
560        if (name.equals(OMEMO_SETTING)) {
561            OmemoSetting.load(this, preferences);
562            changeOmemoSettingSummary();
563        } else if (name.equals(KEEP_FOREGROUND_SERVICE)) {
564            xmppConnectionService.toggleForegroundService();
565        } else if (resendPresence.contains(name)) {
566            if (xmppConnectionServiceBound) {
567                if (name.equals(AWAY_WHEN_SCREEN_IS_OFF) || name.equals(MANUALLY_CHANGE_PRESENCE)) {
568                    xmppConnectionService.toggleScreenEventReceiver();
569                }
570                xmppConnectionService.refreshAllPresences();
571            }
572        } else if (name.equals("dont_trust_system_cas")) {
573            xmppConnectionService.updateMemorizingTrustmanager();
574            reconnectAccounts();
575        } else if (name.equals("use_tor")) {
576            if (preferences.getBoolean(name, false)) {
577                displayToast(getString(R.string.audio_video_disabled_tor));
578            }
579            reconnectAccounts();
580            xmppConnectionService.reinitializeMuclumbusService();
581        } else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) {
582            xmppConnectionService.expireOldMessages(true);
583        } 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")) {
584            final int theme = findTheme();
585            xmppConnectionService.setTheme(theme);
586            ThemeHelper.applyCustomColors(xmppConnectionService);
587            recreate();
588        } else if (name.equals(PREVENT_SCREENSHOTS)) {
589            SettingsUtils.applyScreenshotPreventionSetting(this);
590        } else if (UnifiedPushDistributor.PREFERENCES.contains(name)) {
591            final String pushServerPreference =
592                    Strings.nullToEmpty(preferences.getString(
593                            UnifiedPushDistributor.PREFERENCE_PUSH_SERVER,
594                            getString(R.string.default_push_server))).trim();
595            if (isJidInvalid(pushServerPreference) || isHttpUri(pushServerPreference)) {
596                Toast.makeText(this,R.string.invalid_jid,Toast.LENGTH_LONG).show();
597            }
598            if (xmppConnectionService.reconfigurePushDistributor()) {
599                xmppConnectionService.renewUnifiedPushEndpoints();
600            }
601        }
602    }
603
604    private static boolean isJidInvalid(final String input) {
605        if (Strings.isNullOrEmpty(input)) {
606            return true;
607        }
608        try {
609            Jid.ofEscaped(input);
610            return false;
611        } catch (final IllegalArgumentException e) {
612            return true;
613        }
614    }
615
616    private static boolean isHttpUri(final String input) {
617        final URI uri;
618        try {
619            uri = new URI(input);
620        } catch (final URISyntaxException e) {
621            return false;
622        }
623        return Arrays.asList("http","https").contains(uri.getScheme());
624    }
625
626    @Override
627    public void onResume() {
628        super.onResume();
629        SettingsUtils.applyScreenshotPreventionSetting(this);
630    }
631
632    @Override
633    public void onRequestPermissionsResult(
634            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
635        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
636        if (grantResults.length > 0)
637            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
638                if (requestCode == REQUEST_CREATE_BACKUP) {
639                    createBackup();
640                }
641                if (requestCode == REQUEST_DOWNLOAD_STICKERS) {
642                    downloadStickers();
643                }
644            } else {
645                Toast.makeText(
646                                this,
647                                getString(
648                                        R.string.no_storage_permission,
649                                        getString(R.string.app_name)),
650                                Toast.LENGTH_SHORT)
651                        .show();
652            }
653    }
654
655    private void createBackup() {
656        new AlertDialog.Builder(this)
657            .setTitle("Create Backup")
658            .setMessage("Export extra Cheogram-only data (backup will not import into other apps then)?")
659            .setPositiveButton(R.string.yes, (dialog, whichButton) -> {
660                createBackup(true);
661            })
662            .setNegativeButton(R.string.no, (dialog, whichButton) -> {
663                createBackup(false);
664            }).show();
665    }
666
667    private void createBackup(boolean withCheogramDb) {
668        Intent intent = new Intent(this, ExportBackupService.class);
669        intent.putExtra("cheogram_db", withCheogramDb);
670        ContextCompat.startForegroundService(this, intent);
671        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
672        builder.setMessage(R.string.backup_started_message);
673        builder.setPositiveButton(R.string.ok, null);
674        builder.create().show();
675    }
676
677    private void downloadStickers() {
678        Intent intent = new Intent(this, DownloadDefaultStickers.class);
679        intent.putExtra("tor", xmppConnectionService.useTorToConnect());
680        ContextCompat.startForegroundService(this, intent);
681        displayToast("Sticker download started");
682    }
683
684    private void displayToast(final String msg) {
685        runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show());
686    }
687
688    private void reconnectAccounts() {
689        for (Account account : xmppConnectionService.getAccounts()) {
690            if (account.isEnabled()) {
691                xmppConnectionService.reconnectAccountInBackground(account);
692            }
693        }
694    }
695
696    public void refreshUiReal() {
697        // nothing to do. This Activity doesn't implement any listeners
698    }
699}