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        if (Build.VERSION.SDK_INT > 30 && theTheme.equals("custom")) {
433            final boolean customAutomatic = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("custom_theme_automatic", false);
434            final PreferenceScreen customTheme = (PreferenceScreen) mSettingsFragment.findPreference("custom_theme");
435            final Preference customThemeIsDark = mSettingsFragment.findPreference("custom_theme_dark");
436
437            if (customAutomatic) {
438                if (customTheme != null && customThemeIsDark != null) customTheme.removePreference(customThemeIsDark);
439            } else {
440                final boolean isDark = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("custom_theme_dark", false);
441                if (isDark) {
442                    final PreferenceCategory customThemeColors = (PreferenceCategory) mSettingsFragment.findPreference("custom_theme_colors");
443                    if (customTheme != null && customThemeColors != null) customTheme.removePreference(customThemeColors);
444                } else {
445                    final PreferenceCategory customThemeColorsDark = (PreferenceCategory) mSettingsFragment.findPreference("custom_theme_colors_dark");
446                    if (customTheme != null && customThemeColorsDark != null) customTheme.removePreference(customThemeColorsDark);
447                }
448            }
449        }
450    }
451
452    private void changeOmemoSettingSummary() {
453        final ListPreference omemoPreference =
454                (ListPreference) mSettingsFragment.findPreference(OMEMO_SETTING);
455        if (omemoPreference == null) {
456            return;
457        }
458        final String value = omemoPreference.getValue();
459        switch (value) {
460            case "always":
461                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_always);
462                break;
463            case "default_on":
464                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_on);
465                break;
466            case "default_off":
467                omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_off);
468                break;
469        }
470    }
471
472    private boolean isCallable(final Intent i) {
473        return i != null
474                && getPackageManager()
475                                .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY)
476                                .size()
477                        > 0;
478    }
479
480    private boolean cleanCache() {
481        Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
482        intent.setData(Uri.parse("package:" + getPackageName()));
483        startActivity(intent);
484        return true;
485    }
486
487    private boolean cleanPrivateStorage() {
488        for (String type : Arrays.asList("Images", "Videos", "Files", "Recordings")) {
489            cleanPrivateFiles(type);
490        }
491        return true;
492    }
493
494    private void cleanPrivateFiles(final String type) {
495        try {
496            File dir = new File(getFilesDir().getAbsolutePath(), "/" + type + "/");
497            File[] array = dir.listFiles();
498            if (array != null) {
499                for (int b = 0; b < array.length; b++) {
500                    String name = array[b].getName().toLowerCase();
501                    if (name.equals(".nomedia")) {
502                        continue;
503                    }
504                    if (array[b].isFile()) {
505                        array[b].delete();
506                    }
507                }
508            }
509        } catch (Throwable e) {
510            Log.e("CleanCache", e.toString());
511        }
512    }
513
514    private boolean deleteOmemoIdentities() {
515        AlertDialog.Builder builder = new AlertDialog.Builder(this);
516        builder.setTitle(R.string.pref_delete_omemo_identities);
517        final List<CharSequence> accounts = new ArrayList<>();
518        for (Account account : xmppConnectionService.getAccounts()) {
519            if (account.isEnabled()) {
520                accounts.add(account.getJid().asBareJid().toString());
521            }
522        }
523        final boolean[] checkedItems = new boolean[accounts.size()];
524        builder.setMultiChoiceItems(
525                accounts.toArray(new CharSequence[accounts.size()]),
526                checkedItems,
527                (dialog, which, isChecked) -> {
528                    checkedItems[which] = isChecked;
529                    final AlertDialog alertDialog = (AlertDialog) dialog;
530                    for (boolean item : checkedItems) {
531                        if (item) {
532                            alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
533                            return;
534                        }
535                    }
536                    alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
537                });
538        builder.setNegativeButton(R.string.cancel, null);
539        builder.setPositiveButton(
540                R.string.delete_selected_keys,
541                (dialog, which) -> {
542                    for (int i = 0; i < checkedItems.length; ++i) {
543                        if (checkedItems[i]) {
544                            try {
545                                Jid jid = Jid.of(accounts.get(i).toString());
546                                Account account = xmppConnectionService.findAccountByJid(jid);
547                                if (account != null) {
548                                    account.getAxolotlService().regenerateKeys(true);
549                                }
550                            } catch (IllegalArgumentException e) {
551                                //
552                            }
553                        }
554                    }
555                });
556        AlertDialog dialog = builder.create();
557        dialog.show();
558        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
559        return true;
560    }
561
562    @Override
563    public void onStop() {
564        super.onStop();
565        PreferenceManager.getDefaultSharedPreferences(this)
566                .unregisterOnSharedPreferenceChangeListener(this);
567    }
568
569    @Override
570    public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
571        final List<String> resendPresence =
572                Arrays.asList(
573                        "confirm_messages",
574                        DND_ON_SILENT_MODE,
575                        AWAY_WHEN_SCREEN_IS_OFF,
576                        "allow_message_correction",
577                        TREAT_VIBRATE_AS_SILENT,
578                        MANUALLY_CHANGE_PRESENCE,
579                        BROADCAST_LAST_ACTIVITY);
580        if (name.equals(OMEMO_SETTING)) {
581            OmemoSetting.load(this, preferences);
582            changeOmemoSettingSummary();
583        } else if (name.equals(KEEP_FOREGROUND_SERVICE)) {
584            xmppConnectionService.toggleForegroundService();
585        } else if (resendPresence.contains(name)) {
586            if (xmppConnectionServiceBound) {
587                if (name.equals(AWAY_WHEN_SCREEN_IS_OFF) || name.equals(MANUALLY_CHANGE_PRESENCE)) {
588                    xmppConnectionService.toggleScreenEventReceiver();
589                }
590                xmppConnectionService.refreshAllPresences();
591            }
592        } else if (name.equals("dont_trust_system_cas")) {
593            xmppConnectionService.updateMemorizingTrustmanager();
594            reconnectAccounts();
595        } else if (name.equals("use_tor")) {
596            if (preferences.getBoolean(name, false)) {
597                displayToast(getString(R.string.audio_video_disabled_tor));
598            }
599            reconnectAccounts();
600            xmppConnectionService.reinitializeMuclumbusService();
601        } else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) {
602            xmppConnectionService.expireOldMessages(true);
603        } else if ( name.equals(THEME) ||
604                    name.equals("custom_theme_automatic") ||
605                    name.equals("custom_theme_dark") ||
606                    name.equals("custom_theme_primary") ||
607                    name.equals("custom_theme_primary_dark") ||
608                    name.equals("custom_theme_accent") ||
609                    name.equals("custom_theme_background_primary") ||
610                    name.equals("custom_dark_theme_primary") ||
611                    name.equals("custom_dark_theme_primary_dark") ||
612                    name.equals("custom_dark_theme_accent") ||
613                    name.equals("custom_dark_theme_background_primary"))
614        {
615            final int theme = findTheme();
616            xmppConnectionService.setTheme(theme);
617            ThemeHelper.applyCustomColors(xmppConnectionService);
618            recreate();
619        } else if (name.equals(PREVENT_SCREENSHOTS)) {
620            SettingsUtils.applyScreenshotPreventionSetting(this);
621        } else if (UnifiedPushDistributor.PREFERENCES.contains(name)) {
622            final String pushServerPreference =
623                    Strings.nullToEmpty(preferences.getString(
624                            UnifiedPushDistributor.PREFERENCE_PUSH_SERVER,
625                            getString(R.string.default_push_server))).trim();
626            if (isJidInvalid(pushServerPreference) || isHttpUri(pushServerPreference)) {
627                Toast.makeText(this,R.string.invalid_jid,Toast.LENGTH_LONG).show();
628            }
629            if (xmppConnectionService.reconfigurePushDistributor()) {
630                xmppConnectionService.renewUnifiedPushEndpoints();
631            }
632        }
633    }
634
635    private static boolean isJidInvalid(final String input) {
636        if (Strings.isNullOrEmpty(input)) {
637            return true;
638        }
639        try {
640            Jid.ofEscaped(input);
641            return false;
642        } catch (final IllegalArgumentException e) {
643            return true;
644        }
645    }
646
647    private static boolean isHttpUri(final String input) {
648        final URI uri;
649        try {
650            uri = new URI(input);
651        } catch (final URISyntaxException e) {
652            return false;
653        }
654        return Arrays.asList("http","https").contains(uri.getScheme());
655    }
656
657    @Override
658    public void onResume() {
659        super.onResume();
660        SettingsUtils.applyScreenshotPreventionSetting(this);
661    }
662
663    @Override
664    public void onRequestPermissionsResult(
665            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
666        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
667        if (grantResults.length > 0)
668            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
669                if (requestCode == REQUEST_CREATE_BACKUP) {
670                    createBackup();
671                }
672                if (requestCode == REQUEST_DOWNLOAD_STICKERS) {
673                    downloadStickers();
674                }
675            } else {
676                Toast.makeText(
677                                this,
678                                getString(
679                                        R.string.no_storage_permission,
680                                        getString(R.string.app_name)),
681                                Toast.LENGTH_SHORT)
682                        .show();
683            }
684    }
685
686    private void createBackup() {
687        new AlertDialog.Builder(this)
688            .setTitle("Create Backup")
689            .setMessage("Export extra Cheogram-only data (backup will not import into other apps then)?")
690            .setPositiveButton(R.string.yes, (dialog, whichButton) -> {
691                createBackup(true);
692            })
693            .setNegativeButton(R.string.no, (dialog, whichButton) -> {
694                createBackup(false);
695            }).show();
696    }
697
698    private void createBackup(boolean withCheogramDb) {
699        Intent intent = new Intent(this, ExportBackupService.class);
700        intent.putExtra("cheogram_db", withCheogramDb);
701        ContextCompat.startForegroundService(this, intent);
702        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
703        builder.setMessage(R.string.backup_started_message);
704        builder.setPositiveButton(R.string.ok, null);
705        builder.create().show();
706    }
707
708    private void downloadStickers() {
709        Intent intent = new Intent(this, DownloadDefaultStickers.class);
710        intent.putExtra("tor", xmppConnectionService.useTorToConnect());
711        ContextCompat.startForegroundService(this, intent);
712        displayToast("Sticker download started");
713    }
714
715    private void displayToast(final String msg) {
716        runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show());
717    }
718
719    private void reconnectAccounts() {
720        for (Account account : xmppConnectionService.getAccounts()) {
721            if (account.isEnabled()) {
722                xmppConnectionService.reconnectAccountInBackground(account);
723            }
724        }
725    }
726
727    public void refreshUiReal() {
728        // nothing to do. This Activity doesn't implement any listeners
729    }
730}