Explorar o código

Migrate SettingsController to kotlin + viewbinding

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Andy Scherzinger %!s(int64=3) %!d(string=hai) anos
pai
achega
84fea0ba80

+ 0 - 1031
app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java

@@ -1,1031 +0,0 @@
-/*
- * Nextcloud Talk application
- *
- * @author Andy Scherzinger
- * @author Mario Danic
- * @author Tim Krüger
- * Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
- * Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
- * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.nextcloud.talk.controllers;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.security.KeyChain;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.Checkable;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.bluelinelabs.conductor.Controller;
-import com.bluelinelabs.conductor.RouterTransaction;
-import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
-import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
-import com.facebook.drawee.view.SimpleDraweeView;
-import com.google.android.material.card.MaterialCardView;
-import com.google.android.material.textfield.TextInputLayout;
-import com.nextcloud.talk.BuildConfig;
-import com.nextcloud.talk.R;
-import com.nextcloud.talk.api.NcApi;
-import com.nextcloud.talk.application.NextcloudTalkApplication;
-import com.nextcloud.talk.controllers.base.BaseController;
-import com.nextcloud.talk.jobs.AccountRemovalWorker;
-import com.nextcloud.talk.jobs.ContactAddressBookWorker;
-import com.nextcloud.talk.models.database.CapabilitiesUtil;
-import com.nextcloud.talk.models.database.UserEntity;
-import com.nextcloud.talk.models.json.generic.GenericOverall;
-import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
-import com.nextcloud.talk.utils.ApiUtils;
-import com.nextcloud.talk.utils.DisplayUtils;
-import com.nextcloud.talk.utils.LoggingUtils;
-import com.nextcloud.talk.utils.NotificationUtils;
-import com.nextcloud.talk.utils.SecurityUtils;
-import com.nextcloud.talk.utils.bundle.BundleKeys;
-import com.nextcloud.talk.utils.database.user.UserUtils;
-import com.nextcloud.talk.utils.preferences.AppPreferences;
-import com.nextcloud.talk.utils.preferences.MagicUserInputModule;
-import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder;
-import com.yarolegovich.lovelydialog.LovelySaveStateHandler;
-import com.yarolegovich.lovelydialog.LovelyStandardDialog;
-import com.yarolegovich.mp.MaterialChoicePreference;
-import com.yarolegovich.mp.MaterialEditTextPreference;
-import com.yarolegovich.mp.MaterialPreferenceCategory;
-import com.yarolegovich.mp.MaterialPreferenceScreen;
-import com.yarolegovich.mp.MaterialStandardPreference;
-import com.yarolegovich.mp.MaterialSwitchPreference;
-
-import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.core.content.ContextCompat;
-import androidx.core.view.ViewCompat;
-import androidx.emoji.widget.EmojiTextView;
-import androidx.work.OneTimeWorkRequest;
-import androidx.work.WorkManager;
-import autodagger.AutoInjector;
-import butterknife.BindView;
-import butterknife.OnClick;
-import io.reactivex.Observer;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-import okhttp3.MediaType;
-import okhttp3.RequestBody;
-
-@AutoInjector(NextcloudTalkApplication.class)
-public class SettingsController extends BaseController {
-
-    public static final String TAG = "SettingsController";
-    private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0;
-    @BindView(R.id.settings_screen)
-    MaterialPreferenceScreen settingsScreen;
-    @BindView(R.id.settings_proxy_port_edit)
-    MaterialEditTextPreference proxyPortEditText;
-    @BindView(R.id.settings_licence)
-    MaterialStandardPreference licenceButton;
-    @BindView(R.id.settings_privacy)
-    MaterialStandardPreference privacyButton;
-    @BindView(R.id.settings_source_code)
-    MaterialStandardPreference sourceCodeButton;
-    @BindView(R.id.settings_version)
-    MaterialStandardPreference versionInfo;
-    @BindView(R.id.avatarContainer)
-    RelativeLayout avatarContainer;
-    @BindView(R.id.avatar_image)
-    SimpleDraweeView avatarImageView;
-    @BindView(R.id.display_name_text)
-    EmojiTextView displayNameTextView;
-    @BindView(R.id.base_url_text)
-    TextView baseUrlTextView;
-    @BindView(R.id.server_age_warning_text_card)
-    MaterialCardView serverAgeCardView;
-    @BindView(R.id.server_age_warning_text)
-    TextView serverAgeTextView;
-    @BindView(R.id.server_age_warning_icon)
-    ImageView serverAgeIcon;
-    @BindView(R.id.settings_notifications_category)
-    MaterialPreferenceCategory notificationsCategory;
-    @BindView(R.id.settings_call_sound)
-    MaterialStandardPreference settingsCallSound;
-    @BindView(R.id.settings_message_sound)
-    MaterialStandardPreference settingsMessageSound;
-    @BindView(R.id.settings_remove_account)
-    MaterialStandardPreference removeAccountButton;
-    @BindView(R.id.settings_reauthorize)
-    MaterialStandardPreference reauthorizeButton;
-    @BindView(R.id.message_view)
-    MaterialPreferenceCategory messageView;
-    @BindView(R.id.settings_client_cert)
-    MaterialStandardPreference certificateSetup;
-    @BindView(R.id.settings_incognito_keyboard)
-    MaterialSwitchPreference incognitoKeyboardSwitchPreference;
-    @BindView(R.id.settings_screen_security)
-    MaterialSwitchPreference screenSecuritySwitchPreference;
-    @BindView(R.id.settings_screen_lock)
-    MaterialSwitchPreference screenLockSwitchPreference;
-    @BindView(R.id.settings_screen_lock_timeout)
-    MaterialChoicePreference screenLockTimeoutChoicePreference;
-    @BindView(R.id.settings_phone_book_integration)
-    MaterialSwitchPreference phoneBookIntegrationPreference;
-    @BindView(R.id.settings_read_privacy)
-    MaterialSwitchPreference readPrivacyPreference;
-
-    @BindView(R.id.message_text)
-    TextView messageText;
-    @Inject
-    AppPreferences appPreferences;
-    @Inject
-    NcApi ncApi;
-    @Inject
-    UserUtils userUtils;
-    @Inject
-    Context context;
-    private LovelySaveStateHandler saveStateHandler;
-    private UserEntity currentUser;
-    private String credentials;
-    private OnPreferenceValueChangedListener<String> proxyTypeChangeListener;
-    private OnPreferenceValueChangedListener<Boolean> proxyCredentialsChangeListener;
-    private OnPreferenceValueChangedListener<Boolean> screenSecurityChangeListener;
-    private OnPreferenceValueChangedListener<Boolean> screenLockChangeListener;
-    private OnPreferenceValueChangedListener<String> screenLockTimeoutChangeListener;
-    private OnPreferenceValueChangedListener<String> themeChangeListener;
-    private OnPreferenceValueChangedListener<Boolean> readPrivacyChangeListener;
-    private OnPreferenceValueChangedListener<Boolean> phoneBookIntegrationChangeListener;
-
-    private Disposable profileQueryDisposable;
-    private Disposable dbQueryDisposable;
-
-    @NonNull
-    @Override
-    protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
-        return inflater.inflate(R.layout.controller_settings, container, false);
-    }
-
-    private void getCurrentUser() {
-        currentUser = userUtils.getCurrentUser();
-        credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken());
-    }
-
-    @Override
-    protected void onViewBound(@NonNull View view) {
-        super.onViewBound(view);
-        setHasOptionsMenu(true);
-
-        ViewCompat.setTransitionName(avatarImageView, "userAvatar.transitionTag");
-        NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
-
-        getCurrentUser();
-
-        if (saveStateHandler == null) {
-            saveStateHandler = new LovelySaveStateHandler();
-        }
-
-        appPreferences.registerProxyTypeListener(proxyTypeChangeListener = new ProxyTypeChangeListener());
-        appPreferences.registerProxyCredentialsListener(proxyCredentialsChangeListener = new ProxyCredentialsChangeListener());
-        appPreferences.registerScreenSecurityListener(screenSecurityChangeListener = new ScreenSecurityChangeListener());
-        appPreferences.registerScreenLockListener(screenLockChangeListener = new ScreenLockListener());
-        appPreferences.registerScreenLockTimeoutListener(screenLockTimeoutChangeListener = new ScreenLockTimeoutListener());
-        appPreferences.registerThemeChangeListener(themeChangeListener = new ThemeChangeListener());
-        appPreferences.registerPhoneBookIntegrationChangeListener(
-                phoneBookIntegrationChangeListener = new PhoneBookIntegrationChangeListener(this));
-        appPreferences.registerReadPrivacyChangeListener(readPrivacyChangeListener = new ReadPrivacyChangeListener());
-
-        List<String> listWithIntFields = new ArrayList<>();
-        listWithIntFields.add("proxy_port");
-
-        settingsScreen.setUserInputModule(new MagicUserInputModule(getActivity(), listWithIntFields));
-        settingsScreen.setVisibilityController(R.id.settings_proxy_use_credentials,
-                Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit),
-                true);
-
-        if (!TextUtils.isEmpty(getResources().getString(R.string.nc_gpl3_url))) {
-            licenceButton.addPreferenceClickListener(view1 -> {
-                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().
-                        getString(R.string.nc_gpl3_url)));
-                startActivity(browserIntent);
-            });
-        } else {
-            licenceButton.setVisibility(View.GONE);
-        }
-
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
-            incognitoKeyboardSwitchPreference.setVisibility(View.GONE);
-        }
-
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-            screenLockSwitchPreference.setVisibility(View.GONE);
-            screenLockTimeoutChoicePreference.setVisibility(View.GONE);
-        } else {
-            screenLockSwitchPreference.setSummary(String.format(Locale.getDefault(),
-                    getResources().getString(R.string.nc_settings_screen_lock_desc),
-                    getResources().getString(R.string.nc_app_product_name)));
-        }
-
-        if (!TextUtils.isEmpty(getResources().getString(R.string.nc_privacy_url))) {
-            privacyButton.addPreferenceClickListener(view12 -> {
-                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().
-                        getString(R.string.nc_privacy_url)));
-                startActivity(browserIntent);
-            });
-        } else {
-            privacyButton.setVisibility(View.GONE);
-        }
-
-        if (!TextUtils.isEmpty(getResources().getString(R.string.nc_source_code_url))) {
-            sourceCodeButton.addPreferenceClickListener(view13 -> {
-                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().
-                        getString(R.string.nc_source_code_url)));
-                startActivity(browserIntent);
-            });
-        } else {
-            sourceCodeButton.setVisibility(View.GONE);
-        }
-
-        versionInfo.setSummary("v" + BuildConfig.VERSION_NAME);
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-
-            settingsCallSound.setOnClickListener(v -> {
-                Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
-                intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID);
-                intent.putExtra(Settings.EXTRA_CHANNEL_ID,
-                                NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V4);
-                startActivity(intent);
-            });
-
-            settingsMessageSound.setOnClickListener(v -> {
-                Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
-                intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID);
-                intent.putExtra(Settings.EXTRA_CHANNEL_ID,
-                                NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V4);
-                startActivity(intent);
-            });
-
-        } else {
-
-            settingsCallSound.setOnClickListener(v -> {
-                Bundle bundle = new Bundle();
-                bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), true);
-                getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle))
-                                               .pushChangeHandler(new HorizontalChangeHandler())
-                                               .popChangeHandler(new HorizontalChangeHandler()));
-            });
-
-            settingsMessageSound.setOnClickListener(v -> {
-                Bundle bundle = new Bundle();
-                bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false);
-                getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle))
-                                               .pushChangeHandler(new HorizontalChangeHandler())
-                                               .popChangeHandler(new HorizontalChangeHandler()));
-            });
-
-        }
-
-        if (CapabilitiesUtil.isPhoneBookIntegrationAvailable(userUtils.getCurrentUser())) {
-            phoneBookIntegrationPreference.setVisibility(View.VISIBLE);
-        } else {
-            phoneBookIntegrationPreference.setVisibility(View.GONE);
-        }
-
-        String host = null;
-        int port = -1;
-
-        URI uri;
-        try {
-            uri = new URI(currentUser.getBaseUrl());
-            host = uri.getHost();
-            port = uri.getPort();
-        } catch (URISyntaxException e) {
-            Log.e(TAG, "Failed to create uri");
-        }
-
-        String finalHost = host;
-        int finalPort = port;
-        certificateSetup.addPreferenceClickListener(v -> KeyChain.choosePrivateKeyAlias(Objects.requireNonNull(getActivity()), alias -> {
-            String finalAlias = alias;
-            getActivity().runOnUiThread(() -> {
-                if (finalAlias != null) {
-                    certificateSetup.setTitle(R.string.nc_client_cert_change);
-                } else {
-                    certificateSetup.setTitle(R.string.nc_client_cert_setup);
-                }
-            });
-
-            if (alias == null) {
-                alias = "";
-            }
-
-            userUtils.createOrUpdateUser(null, null, null, null, null, null, null, currentUser.getId(),
-                    null, alias, null);
-        }, new String[]{"RSA", "EC"}, null, finalHost, finalPort, currentUser.getClientCertificate
-                ()));
-    }
-
-    private void showLovelyDialog(int dialogId, Bundle savedInstanceState) {
-        if (dialogId == ID_REMOVE_ACCOUNT_WARNING_DIALOG) {
-            showRemoveAccountWarning(savedInstanceState);
-        }
-    }
-
-    @OnClick(R.id.settings_version)
-    void sendLogs() {
-        if (getResources().getBoolean(R.bool.nc_is_debug)) {
-            LoggingUtils.INSTANCE.sendMailWithAttachment(context);
-        }
-    }
-
-    @Override
-    protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
-        saveStateHandler.saveInstanceState(outState);
-        super.onSaveViewState(view, outState);
-    }
-
-    @Override
-    protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
-        super.onRestoreViewState(view, savedViewState);
-        if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) {
-            //Dialog won't be restarted automatically, so we need to call this method.
-            //Each dialog knows how to restore its state
-            showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState);
-        }
-    }
-
-    private void showRemoveAccountWarning(Bundle savedInstanceState) {
-        if (getActivity() != null) {
-            new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL)
-                    .setTopColorRes(R.color.nc_darkRed)
-                    .setIcon(DisplayUtils.getTintedDrawable(getResources(),
-                            R.drawable.ic_delete_black_24dp, R.color.bg_default))
-                    .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed))
-                    .setTitle(R.string.nc_settings_remove_account)
-                    .setMessage(R.string.nc_settings_remove_confirmation)
-                    .setPositiveButton(R.string.nc_settings_remove, new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            removeCurrentAccount();
-                        }
-                    })
-                    .setNegativeButton(R.string.nc_cancel, null)
-                    .setInstanceStateHandler(ID_REMOVE_ACCOUNT_WARNING_DIALOG, saveStateHandler)
-                    .setSavedInstanceState(savedInstanceState)
-                    .show();
-        }
-    }
-
-    private void removeCurrentAccount() {
-        boolean otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser.getId());
-
-        OneTimeWorkRequest accountRemovalWork = new OneTimeWorkRequest.Builder(AccountRemovalWorker.class).build();
-        WorkManager.getInstance().enqueue(accountRemovalWork);
-
-        if (otherUserExists && getView() != null) {
-            onViewBound(getView());
-            onAttach(getView());
-        } else if (!otherUserExists) {
-            getRouter().setRoot(RouterTransaction.with(
-                    new ServerSelectionController())
-                    .pushChangeHandler(new VerticalChangeHandler())
-                    .popChangeHandler(new VerticalChangeHandler()));
-        }
-    }
-
-    private String getRingtoneName(Context context, Uri ringtoneUri) {
-        if (ringtoneUri == null) {
-            return getResources().getString(R.string.nc_settings_no_ringtone);
-        } else if (NotificationUtils.DEFAULT_CALL_RINGTONE_URI.equals(ringtoneUri.toString()) ||
-            NotificationUtils.DEFAULT_MESSAGE_RINGTONE_URI.equals(ringtoneUri.toString())) {
-            return getResources().getString(R.string.nc_settings_default_ringtone);
-        } else  {
-            Ringtone r = RingtoneManager.getRingtone(context, ringtoneUri);
-            return r.getTitle(context);
-        }
-    }
-
-    @Override
-    protected void onAttach(@NonNull View view) {
-        super.onAttach(view);
-
-        if (getActionBar() != null) {
-            getActionBar().show();
-        }
-
-        dispose(null);
-        getCurrentUser();
-
-        if (!TextUtils.isEmpty(currentUser.getClientCertificate())) {
-            certificateSetup.setTitle(R.string.nc_client_cert_change);
-        } else {
-            certificateSetup.setTitle(R.string.nc_client_cert_setup);
-        }
-
-        ((Checkable) screenSecuritySwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenSecured());
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito());
-        }
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito());
-        }
-
-        if (CapabilitiesUtil.isReadStatusAvailable(userUtils.getCurrentUser())) {
-            ((Checkable) readPrivacyPreference.findViewById(R.id.mp_checkable)).setChecked(!CapabilitiesUtil.isReadStatusPrivate(currentUser));
-        } else {
-            readPrivacyPreference.setVisibility(View.GONE);
-        }
-
-        ((Checkable) phoneBookIntegrationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled());
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
-
-            if (keyguardManager.isKeyguardSecure()) {
-                screenLockSwitchPreference.setEnabled(true);
-                screenLockTimeoutChoicePreference.setEnabled(true);
-                ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenLocked());
-
-                screenLockTimeoutChoicePreference.setEnabled(appPreferences.getIsScreenLocked());
-
-                if (appPreferences.getIsScreenLocked()) {
-                    screenLockTimeoutChoicePreference.setAlpha(1.0f);
-                } else {
-                    screenLockTimeoutChoicePreference.setAlpha(0.38f);
-                }
-
-                screenLockSwitchPreference.setAlpha(1.0f);
-            } else {
-                screenLockSwitchPreference.setEnabled(false);
-                screenLockTimeoutChoicePreference.setEnabled(false);
-                appPreferences.removeScreenLock();
-                appPreferences.removeScreenLockTimeout();
-                ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(false);
-                screenLockSwitchPreference.setAlpha(0.38f);
-                screenLockTimeoutChoicePreference.setAlpha(0.38f);
-            }
-        }
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            notificationsCategory.setTitle(getResources().getString(R.string.nc_settings_notification_sounds_post_oreo));
-        }
-
-        Uri callRingtoneUri = NotificationUtils.INSTANCE.getCallRingtoneUri(view.getContext(), appPreferences);
-        settingsCallSound.setSummary(getRingtoneName(view.getContext(), callRingtoneUri));
-
-        Uri messageRingtoneUri = NotificationUtils.INSTANCE.getMessageRingtoneUri(view.getContext(), appPreferences);
-        settingsMessageSound.setSummary(getRingtoneName(view.getContext(), messageRingtoneUri));
-
-        if ("No proxy".equals(appPreferences.getProxyType()) || appPreferences.getProxyType() == null) {
-            hideProxySettings();
-        } else {
-            showProxySettings();
-        }
-
-        if (appPreferences.getProxyCredentials()) {
-            showProxyCredentials();
-        } else {
-            hideProxyCredentials();
-        }
-
-        if (currentUser != null) {
-
-            baseUrlTextView.setText(Uri.parse(currentUser.getBaseUrl()).getHost());
-
-            if (CapabilitiesUtil.isServerEOL(currentUser)) {
-                serverAgeTextView.setTextColor(ContextCompat.getColor(context, R.color.nc_darkRed));
-                serverAgeTextView.setText(R.string.nc_settings_server_eol);
-                serverAgeIcon.setColorFilter(ContextCompat.getColor(context, R.color.nc_darkRed),
-                                             PorterDuff.Mode.SRC_IN);
-            } else if (CapabilitiesUtil.isServerAlmostEOL(currentUser)) {
-                serverAgeTextView.setTextColor(ContextCompat.getColor(context, R.color.nc_darkYellow));
-                serverAgeTextView.setText(R.string.nc_settings_server_almost_eol);
-                serverAgeIcon.setColorFilter(ContextCompat.getColor(context, R.color.nc_darkYellow),
-                                             PorterDuff.Mode.SRC_IN);
-            } else {
-                serverAgeCardView.setVisibility(View.GONE);
-            }
-
-            reauthorizeButton.addPreferenceClickListener(view14 -> {
-                getRouter().pushController(RouterTransaction.with(
-                        new WebViewLoginController(currentUser.getBaseUrl(), true))
-                        .pushChangeHandler(new VerticalChangeHandler())
-                        .popChangeHandler(new VerticalChangeHandler()));
-            });
-
-            if (currentUser.getDisplayName() != null) {
-                displayNameTextView.setText(currentUser.getDisplayName());
-            }
-            
-            DisplayUtils.loadAvatarImage(currentUser, avatarImageView, false);
-
-            profileQueryDisposable = ncApi.getUserProfile(credentials,
-                    ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()))
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(userProfileOverall -> {
-
-                        String displayName = null;
-                        if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData()
-                                .getDisplayName())) {
-                            displayName = userProfileOverall.getOcs().getData()
-                                    .getDisplayName();
-                        } else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData()
-                                .getDisplayNameAlt())) {
-                            displayName = userProfileOverall.getOcs().getData()
-                                    .getDisplayNameAlt();
-                        }
-
-
-                        if ((!TextUtils.isEmpty(displayName) && !displayName.equals(currentUser.getDisplayName()))) {
-
-                            dbQueryDisposable = userUtils.createOrUpdateUser(null,
-                                    null,
-                                    null, displayName, null, null,
-                                    null, currentUser.getId(), null, null, null)
-                                    .subscribeOn(Schedulers.io())
-                                    .observeOn(AndroidSchedulers.mainThread())
-                                    .subscribe(userEntityResult -> {
-                                                displayNameTextView.setText(userEntityResult.getDisplayName());
-                                            },
-                                            throwable -> {
-                                                dispose(dbQueryDisposable);
-                                            }, () -> dispose(dbQueryDisposable));
-
-                        }
-                    }, throwable -> {
-                        dispose(profileQueryDisposable);
-                    }, () -> dispose(profileQueryDisposable));
-
-
-            removeAccountButton.addPreferenceClickListener(view1 -> {
-                showLovelyDialog(ID_REMOVE_ACCOUNT_WARNING_DIALOG, null);
-            });
-        }
-
-        if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) {
-            switch (ApplicationWideMessageHolder.getInstance().getMessageType()) {
-                case ACCOUNT_UPDATED_NOT_ADDED:
-                    messageText.setTextColor(getResources().getColor(R.color.colorPrimary));
-                    messageText.setText(getResources().getString(R.string.nc_settings_account_updated));
-                    messageView.setVisibility(View.VISIBLE);
-                    break;
-                case SERVER_WITHOUT_TALK:
-                    messageText.setTextColor(getResources().getColor(R.color.nc_darkRed));
-                    messageText.setText(getResources().getString(R.string.nc_settings_wrong_account));
-                    messageView.setVisibility(View.VISIBLE);
-                case ACCOUNT_WAS_IMPORTED:
-                    messageText.setTextColor(getResources().getColor(R.color.colorPrimary));
-                    messageText.setText(getResources().getString(R.string.nc_Server_account_imported));
-                    messageView.setVisibility(View.VISIBLE);
-                    break;
-                case FAILED_TO_IMPORT_ACCOUNT:
-                    messageText.setTextColor(getResources().getColor(R.color.nc_darkRed));
-                    messageText.setText(getResources().getString(R.string.nc_server_failed_to_import_account));
-                    messageView.setVisibility(View.VISIBLE);
-                    break;
-                default:
-                    messageView.setVisibility(View.GONE);
-                    break;
-            }
-            ApplicationWideMessageHolder.getInstance().setMessageType(null);
-
-            messageView.animate()
-                    .translationY(0)
-                    .alpha(0.0f)
-                    .setDuration(2500)
-                    .setStartDelay(5000)
-                    .setListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            super.onAnimationEnd(animation);
-                            if (messageView != null) {
-                                messageView.setVisibility(View.GONE);
-                            }
-                        }
-                    });
-
-        } else {
-            if (messageView != null) {
-                messageView.setVisibility(View.GONE);
-            }
-        }
-
-        avatarContainer.setOnClickListener(v ->
-                getRouter()
-                        .pushController((RouterTransaction.with(new ProfileController())
-                                .pushChangeHandler(new HorizontalChangeHandler())
-                                .popChangeHandler(new HorizontalChangeHandler()))));
-    }
-
-    @Override
-    public void onDestroy() {
-        if (appPreferences != null) {
-            appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener);
-            appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener);
-            appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener);
-            appPreferences.unregisterScreenLockListener(screenLockChangeListener);
-            appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener);
-            appPreferences.unregisterThemeChangeListener(themeChangeListener);
-            appPreferences.unregisterReadPrivacyChangeListener(readPrivacyChangeListener);
-            appPreferences.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener);
-        }
-        super.onDestroy();
-    }
-
-
-    private void hideProxySettings() {
-        appPreferences.removeProxyHost();
-        appPreferences.removeProxyPort();
-        appPreferences.removeProxyCredentials();
-        appPreferences.removeProxyUsername();
-        appPreferences.removeProxyPassword();
-        settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.GONE);
-        settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.GONE);
-        settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.GONE);
-        settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE);
-        settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE);
-    }
-
-    private void showProxySettings() {
-        settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.VISIBLE);
-        settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.VISIBLE);
-        settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.VISIBLE);
-    }
-
-    private void showProxyCredentials() {
-        settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.VISIBLE);
-        settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.VISIBLE);
-    }
-
-    private void hideProxyCredentials() {
-        appPreferences.removeProxyUsername();
-        appPreferences.removeProxyPassword();
-        settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE);
-        settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE);
-    }
-
-    private void dispose(@Nullable Disposable disposable) {
-        if (disposable != null && !disposable.isDisposed()) {
-            disposable.dispose();
-        } else if (disposable == null) {
-
-            if (profileQueryDisposable != null && !profileQueryDisposable.isDisposed()) {
-                profileQueryDisposable.dispose();
-                profileQueryDisposable = null;
-            } else if (profileQueryDisposable != null) {
-                profileQueryDisposable = null;
-            }
-
-            if (dbQueryDisposable != null && !dbQueryDisposable.isDisposed()) {
-                dbQueryDisposable.dispose();
-                dbQueryDisposable = null;
-            } else if (dbQueryDisposable != null) {
-                dbQueryDisposable = null;
-            }
-        }
-    }
-
-    @Override
-    protected String getTitle() {
-        return getResources().getString(R.string.nc_settings);
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-        if (requestCode == ContactAddressBookWorker.REQUEST_PERMISSION &&
-                grantResults.length > 0 &&
-                grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-            WorkManager
-                    .getInstance()
-                    .enqueue(new OneTimeWorkRequest.Builder(ContactAddressBookWorker.class).build());
-            checkForPhoneNumber();
-        } else {
-            appPreferences.setPhoneBookIntegration(false);
-            ((Checkable) phoneBookIntegrationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled());
-            Toast.makeText(context, context.getResources().getString(
-                    R.string.no_phone_book_integration_due_to_permissions),
-                    Toast.LENGTH_LONG).show();
-        }
-    }
-
-    private class ScreenLockTimeoutListener implements OnPreferenceValueChangedListener<String> {
-
-        @Override
-        public void onChanged(String newValue) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-                SecurityUtils.createKey(appPreferences.getScreenLockTimeout());
-            }
-        }
-    }
-
-    private class ScreenLockListener implements OnPreferenceValueChangedListener<Boolean> {
-
-        @Override
-        public void onChanged(Boolean newValue) {
-            screenLockTimeoutChoicePreference.setEnabled(newValue);
-
-            if (newValue) {
-                screenLockTimeoutChoicePreference.setAlpha(1.0f);
-            } else {
-                screenLockTimeoutChoicePreference.setAlpha(0.38f);
-            }
-        }
-    }
-
-    private class ScreenSecurityChangeListener implements OnPreferenceValueChangedListener<Boolean> {
-
-        @Override
-        public void onChanged(Boolean newValue) {
-            if (newValue) {
-                if (getActivity() != null) {
-                    getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-                }
-            } else {
-                if (getActivity() != null) {
-                    getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
-                }
-            }
-        }
-    }
-
-    private class ProxyCredentialsChangeListener implements OnPreferenceValueChangedListener<Boolean> {
-
-        @Override
-        public void onChanged(Boolean newValue) {
-            if (newValue) {
-                showProxyCredentials();
-            } else {
-                hideProxyCredentials();
-            }
-        }
-    }
-
-    private class ProxyTypeChangeListener implements OnPreferenceValueChangedListener<String> {
-
-        @Override
-        public void onChanged(String newValue) {
-            if ("No proxy".equals(newValue)) {
-                hideProxySettings();
-            } else {
-                switch (newValue) {
-                    case "HTTP":
-                        if (proxyPortEditText != null) {
-                            proxyPortEditText.setValue("3128");
-                        }
-                        break;
-                    case "DIRECT":
-                        if (proxyPortEditText != null) {
-                            proxyPortEditText.setValue("8080");
-                        }
-                        break;
-                    case "SOCKS":
-                        if (proxyPortEditText != null) {
-                            proxyPortEditText.setValue("1080");
-                        }
-                        break;
-                    default:
-                        break;
-                }
-
-                showProxySettings();
-            }
-        }
-    }
-
-    private class ThemeChangeListener implements OnPreferenceValueChangedListener<String> {
-        @Override
-        public void onChanged(String newValue) {
-            NextcloudTalkApplication.Companion.setAppTheme(newValue);
-        }
-    }
-
-    private class PhoneBookIntegrationChangeListener implements OnPreferenceValueChangedListener<Boolean> {
-        private final Controller controller;
-
-        public PhoneBookIntegrationChangeListener(Controller controller) {
-            this.controller = controller;
-        }
-
-        @Override
-        public void onChanged(Boolean isEnabled) {
-            if (isEnabled) {
-                if(ContactAddressBookWorker.Companion.checkPermission(controller, context)){
-                    checkForPhoneNumber();
-                }
-            } else {
-                ContactAddressBookWorker.Companion.deleteAll();
-            }
-        }
-    }
-
-    private void checkForPhoneNumber() {
-        ncApi.getUserData(
-                ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
-                ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl())
-        ).subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(new Observer<UserProfileOverall>() {
-
-                    @Override
-                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
-
-                    }
-
-                    @Override
-                    public void onNext(@io.reactivex.annotations.NonNull UserProfileOverall userProfileOverall) {
-                        if (userProfileOverall.getOcs().getData().getPhone().isEmpty()) {
-                            askForPhoneNumber();
-                        } else {
-                            Log.d(TAG, "phone number already set");
-                        }
-                    }
-
-                    @Override
-                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
-
-                    }
-
-                    @Override
-                    public void onComplete() {
-
-                    }
-                });
-    }
-
-    private void askForPhoneNumber() {
-        final LinearLayout phoneNumberLayoutWrapper = new LinearLayout(getActivity());
-        phoneNumberLayoutWrapper.setOrientation(LinearLayout.VERTICAL);
-        phoneNumberLayoutWrapper.setPadding(50, 0, 50, 0);
-
-        final TextInputLayout phoneNumberInputLayout = new TextInputLayout(getActivity());
-        final EditText phoneNumberField = new EditText(getActivity());
-
-        phoneNumberInputLayout.setHelperTextColor(ColorStateList.valueOf(getResources().getColor(R.color.nc_darkRed)));
-        phoneNumberField.setInputType(InputType.TYPE_CLASS_PHONE);
-        phoneNumberField.setText("+");
-        phoneNumberField.addTextChangedListener(new TextWatcher() {
-            public void afterTextChanged(Editable s) {
-            }
-
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            }
-
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                phoneNumberInputLayout.setHelperText("");
-            }
-        });
-
-        phoneNumberInputLayout.addView(phoneNumberField);
-        phoneNumberLayoutWrapper.addView(phoneNumberInputLayout);
-
-        AlertDialog dialog = new AlertDialog.Builder(getActivity())
-                .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title)
-                .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description)
-                .setView(phoneNumberLayoutWrapper)
-                .setPositiveButton(context.getResources().getString(R.string.nc_common_set), null)
-                .setNegativeButton(context.getResources().getString(R.string.nc_common_skip), null)
-                .create();
-
-        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
-            @Override
-            public void onShow(DialogInterface dialogInterface) {
-                Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
-                button.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        setPhoneNumber(phoneNumberInputLayout, dialog);
-                    }
-                });
-            }
-        });
-        dialog.show();
-    }
-
-    private void setPhoneNumber(TextInputLayout textInputLayout, AlertDialog dialog) {
-        String phoneNumber = textInputLayout.getEditText().getText().toString();
-
-        ncApi.setUserData(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
-                ApiUtils.getUrlForUserData(currentUser.getBaseUrl(), currentUser.getUserId()), "phone", phoneNumber
-        ).subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(new Observer<GenericOverall>() {
-
-                    @Override
-                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
-
-                    }
-
-                    @Override
-                    public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) {
-                        int statusCode = Objects.requireNonNull(genericOverall.getMeta()).getStatusCode();
-                        if (statusCode == 200) {
-                            dialog.dismiss();
-                            Toast.makeText(context, context.getResources().getString(
-                                    R.string.nc_settings_phone_book_integration_phone_number_dialog_success),
-                                    Toast.LENGTH_LONG).show();
-                        } else {
-                            textInputLayout.setHelperText(context.getResources().getString(
-                                    R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid));
-                            Log.d(TAG, "failed to set phoneNumber. statusCode=" + statusCode);
-                        }
-
-                    }
-
-                    @Override
-                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
-                        textInputLayout.setHelperText(context.getResources().getString(
-                                R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid));
-                        Log.e(TAG, "setPhoneNumber error", e);
-                    }
-
-                    @Override
-                    public void onComplete() {
-                    }
-                });
-    }
-
-    private class ReadPrivacyChangeListener implements OnPreferenceValueChangedListener<Boolean> {
-        @Override
-        public void onChanged(Boolean newValue) {
-            String booleanValue = newValue ? "0" : "1";
-            String json = "{\"key\": \"read_status_privacy\", \"value\" : " + booleanValue + "}";
-
-            ncApi.setReadStatusPrivacy(
-                    ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
-                    ApiUtils.getUrlForUserSettings(currentUser.getBaseUrl()),
-                    RequestBody.create(MediaType.parse("application/json"), json))
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(new Observer<GenericOverall>() {
-                        @Override
-                        public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
-                        }
-
-                        @Override
-                        public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) {
-                        }
-
-                        @Override
-                        public void onError(@io.reactivex.annotations.NonNull Throwable e) {
-                            appPreferences.setReadPrivacy(!newValue);
-                            ((Checkable) readPrivacyPreference.findViewById(R.id.mp_checkable)).setChecked(!newValue);
-                        }
-
-                        @Override
-                        public void onComplete() {
-                        }
-                    });
-        }
-    }
-}

+ 1065 - 0
app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt

@@ -0,0 +1,1065 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Andy Scherzinger
+ * @author Mario Danic
+ * @author Tim Krüger
+ * Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
+ * Copyright (C) 2021-2022 Andy Scherzinger <info@andy-scherzinger.de>
+ * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.talk.controllers
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.app.KeyguardManager
+import android.content.Context
+import android.content.DialogInterface
+import android.content.DialogInterface.OnShowListener
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.res.ColorStateList
+import android.graphics.PorterDuff
+import android.media.RingtoneManager
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.provider.Settings
+import android.security.KeyChain
+import android.text.Editable
+import android.text.InputType
+import android.text.TextUtils
+import android.text.TextWatcher
+import android.util.Log
+import android.view.View
+import android.view.WindowManager
+import android.widget.Checkable
+import android.widget.EditText
+import android.widget.LinearLayout
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.core.view.ViewCompat
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import autodagger.AutoInjector
+import com.bluelinelabs.conductor.Controller
+import com.bluelinelabs.conductor.RouterTransaction
+import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
+import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
+import com.google.android.material.textfield.TextInputLayout
+import com.nextcloud.talk.BuildConfig
+import com.nextcloud.talk.R
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.setAppTheme
+import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
+import com.nextcloud.talk.controllers.base.NewBaseController
+import com.nextcloud.talk.controllers.util.viewBinding
+import com.nextcloud.talk.databinding.ControllerSettingsBinding
+import com.nextcloud.talk.jobs.AccountRemovalWorker
+import com.nextcloud.talk.jobs.ContactAddressBookWorker
+import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.checkPermission
+import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.deleteAll
+import com.nextcloud.talk.models.database.CapabilitiesUtil
+import com.nextcloud.talk.models.database.UserEntity
+import com.nextcloud.talk.models.json.generic.GenericOverall
+import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.DisplayUtils
+import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment
+import com.nextcloud.talk.utils.NotificationUtils
+import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
+import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri
+import com.nextcloud.talk.utils.SecurityUtils
+import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ARE_CALL_SOUNDS
+import com.nextcloud.talk.utils.database.user.UserUtils
+import com.nextcloud.talk.utils.preferences.MagicUserInputModule
+import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
+import com.yarolegovich.lovelydialog.LovelySaveStateHandler
+import com.yarolegovich.lovelydialog.LovelyStandardDialog
+import io.reactivex.Observer
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.RequestBody
+import java.net.URI
+import java.net.URISyntaxException
+import java.util.ArrayList
+import java.util.Arrays
+import java.util.Locale
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class SettingsController : NewBaseController(R.layout.controller_settings) {
+    private val binding: ControllerSettingsBinding by viewBinding(ControllerSettingsBinding::bind)
+
+    @Inject
+    lateinit var ncApi: NcApi
+
+    @Inject
+    lateinit var userUtils: UserUtils
+
+    private var saveStateHandler: LovelySaveStateHandler? = null
+    private var currentUser: UserEntity? = null
+    private var credentials: String? = null
+    private var proxyTypeChangeListener: OnPreferenceValueChangedListener<String>? = null
+    private var proxyCredentialsChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
+    private var screenSecurityChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
+    private var screenLockChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
+    private var screenLockTimeoutChangeListener: OnPreferenceValueChangedListener<String?>? = null
+    private var themeChangeListener: OnPreferenceValueChangedListener<String?>? = null
+    private var readPrivacyChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
+    private var phoneBookIntegrationChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
+    private var profileQueryDisposable: Disposable? = null
+    private var dbQueryDisposable: Disposable? = null
+
+    override val title: String
+        get() =
+            resources!!.getString(R.string.nc_settings)
+
+    private fun getCurrentUser() {
+        currentUser = userUtils.currentUser
+        credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
+    }
+
+    override fun onViewBound(view: View) {
+        super.onViewBound(view)
+        setHasOptionsMenu(true)
+        sharedApplication!!.componentApplication.inject(this)
+
+        ViewCompat.setTransitionName((binding.avatarImage), "userAvatar.transitionTag")
+        getCurrentUser()
+
+        if (saveStateHandler == null) {
+            saveStateHandler = LovelySaveStateHandler()
+        }
+
+        registerChangeListeners()
+
+        setupSettingsScreen()
+        setupLicenceSetting()
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            binding.settingsIncognitoKeyboard.visibility = View.GONE
+        }
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            binding.settingsScreenLock.visibility = View.GONE
+            binding.settingsScreenLockTimeout.visibility = View.GONE
+        } else {
+            binding.settingsScreenLock.setSummary(
+                String.format(
+                    Locale.getDefault(),
+                    resources!!.getString(R.string.nc_settings_screen_lock_desc),
+                    resources!!.getString(R.string.nc_app_product_name)
+                )
+            )
+        }
+
+        setupPrivacyUrl()
+        setupSourceCodeUrl()
+        binding.settingsVersion.setSummary("v" + BuildConfig.VERSION_NAME)
+
+        setupSoundSettings()
+
+        setupPhoneBookIntegration()
+
+        setupClientCertView()
+    }
+
+    private fun setupPhoneBookIntegration() {
+        if (CapabilitiesUtil.isPhoneBookIntegrationAvailable(userUtils.currentUser)) {
+            binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
+        } else {
+            binding.settingsPhoneBookIntegration.visibility = View.GONE
+        }
+    }
+
+    private fun setupSoundSettings() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            binding.settingsCallSound.setOnClickListener {
+                val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
+                intent.putExtra(
+                    Settings.EXTRA_CHANNEL_ID,
+                    NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V4
+                )
+
+                startActivity(intent)
+            }
+            binding.settingsMessageSound.setOnClickListener {
+                val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
+                intent.putExtra(
+                    Settings.EXTRA_CHANNEL_ID,
+                    NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V4
+                )
+                startActivity(intent)
+            }
+        } else {
+            binding.settingsCallSound.setOnClickListener {
+                val bundle = Bundle()
+                bundle.putBoolean(KEY_ARE_CALL_SOUNDS, true)
+
+                router.pushController(
+                    RouterTransaction.with(RingtoneSelectionController(bundle))
+                        .pushChangeHandler(HorizontalChangeHandler())
+                        .popChangeHandler(HorizontalChangeHandler())
+                )
+            }
+            binding.settingsMessageSound.setOnClickListener {
+                val bundle = Bundle()
+                bundle.putBoolean(KEY_ARE_CALL_SOUNDS, false)
+
+                router.pushController(
+                    RouterTransaction.with(RingtoneSelectionController(bundle))
+                        .pushChangeHandler(HorizontalChangeHandler())
+                        .popChangeHandler(HorizontalChangeHandler())
+                )
+            }
+        }
+    }
+
+    private fun setupSourceCodeUrl() {
+        if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_source_code_url))) {
+            binding.settingsSourceCode.addPreferenceClickListener {
+                startActivity(
+                    Intent(
+                        Intent.ACTION_VIEW,
+                        Uri.parse(resources!!.getString(R.string.nc_source_code_url))
+                    )
+                )
+            }
+        } else {
+            binding.settingsSourceCode.visibility = View.GONE
+        }
+    }
+
+    private fun setupPrivacyUrl() {
+        if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_privacy_url))) {
+            binding.settingsPrivacy.addPreferenceClickListener {
+                startActivity(
+                    Intent(
+                        Intent.ACTION_VIEW,
+                        Uri.parse(resources!!.getString(R.string.nc_privacy_url))
+                    )
+                )
+            }
+        } else {
+            binding.settingsPrivacy.visibility = View.GONE
+        }
+    }
+
+    private fun setupLicenceSetting() {
+        if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_gpl3_url))) {
+            binding.settingsLicence.addPreferenceClickListener {
+                startActivity(
+                    Intent(
+                        Intent.ACTION_VIEW,
+                        Uri.parse(resources!!.getString(R.string.nc_gpl3_url))
+                    )
+                )
+            }
+        } else {
+            binding.settingsLicence.visibility = View.GONE
+        }
+    }
+
+    private fun setupSettingsScreen() {
+        val listWithIntFields: MutableList<String> = ArrayList()
+        listWithIntFields.add("proxy_port")
+        binding.settingsScreen.setUserInputModule(MagicUserInputModule(activity, listWithIntFields))
+        binding.settingsScreen.setVisibilityController(
+            R.id.settings_proxy_use_credentials,
+            Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit),
+            true
+        )
+    }
+
+    private fun setupClientCertView() {
+        var host: String? = null
+        var port = -1
+        val uri: URI
+        try {
+            uri = URI(currentUser!!.baseUrl)
+            host = uri.host
+            port = uri.port
+        } catch (e: URISyntaxException) {
+            Log.e(TAG, "Failed to create uri")
+        }
+
+        binding.settingsClientCert.addPreferenceClickListener {
+            KeyChain.choosePrivateKeyAlias(
+                activity!!,
+                { alias: String? ->
+                    var finalAlias: String? = alias
+
+                    activity!!.runOnUiThread {
+                        if (finalAlias != null) {
+                            binding.settingsClientCert.setTitle(R.string.nc_client_cert_change)
+                        } else {
+                            binding.settingsClientCert.setTitle(R.string.nc_client_cert_setup)
+                        }
+                    }
+
+                    if (finalAlias == null) {
+                        finalAlias = ""
+                    }
+
+                    userUtils.createOrUpdateUser(
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        currentUser!!.id,
+                        null,
+                        finalAlias,
+                        null
+                    )
+                },
+                arrayOf("RSA", "EC"),
+                null,
+                host,
+                port,
+                currentUser!!.clientCertificate
+            )
+        }
+    }
+
+    private fun registerChangeListeners() {
+        appPreferences!!.registerProxyTypeListener(ProxyTypeChangeListener().also { proxyTypeChangeListener = it })
+        appPreferences!!.registerProxyCredentialsListener(
+            ProxyCredentialsChangeListener().also {
+                proxyCredentialsChangeListener = it
+            }
+        )
+        appPreferences!!.registerScreenSecurityListener(
+            ScreenSecurityChangeListener().also {
+                screenSecurityChangeListener = it
+            }
+        )
+        appPreferences!!.registerScreenLockListener(ScreenLockListener().also { screenLockChangeListener = it })
+        appPreferences!!.registerScreenLockTimeoutListener(
+            ScreenLockTimeoutListener().also {
+                screenLockTimeoutChangeListener = it
+            }
+        )
+        appPreferences!!.registerThemeChangeListener(ThemeChangeListener().also { themeChangeListener = it })
+        appPreferences!!.registerPhoneBookIntegrationChangeListener(
+            PhoneBookIntegrationChangeListener(this).also {
+                phoneBookIntegrationChangeListener = it
+            }
+        )
+        appPreferences!!.registerReadPrivacyChangeListener(
+            ReadPrivacyChangeListener().also {
+                readPrivacyChangeListener = it
+            }
+        )
+    }
+
+    private fun showLovelyDialog(dialogId: Int, savedInstanceState: Bundle?) {
+        if (dialogId == ID_REMOVE_ACCOUNT_WARNING_DIALOG) {
+            showRemoveAccountWarning(savedInstanceState)
+        }
+    }
+
+    fun sendLogs() {
+        if (resources!!.getBoolean(R.bool.nc_is_debug)) {
+            sendMailWithAttachment((context)!!)
+        }
+    }
+
+    override fun onSaveViewState(view: View, outState: Bundle) {
+        saveStateHandler!!.saveInstanceState(outState)
+        super.onSaveViewState(view, outState)
+    }
+
+    override fun onRestoreViewState(view: View, savedViewState: Bundle) {
+        super.onRestoreViewState(view, savedViewState)
+        if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) {
+            // Dialog won't be restarted automatically, so we need to call this method.
+            // Each dialog knows how to restore its state
+            showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState)
+        }
+    }
+
+    private fun showRemoveAccountWarning(savedInstanceState: Bundle?) {
+        if (activity != null) {
+            LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL)
+                .setTopColorRes(R.color.nc_darkRed)
+                .setIcon(
+                    DisplayUtils.getTintedDrawable(
+                        resources,
+                        R.drawable.ic_delete_black_24dp, R.color.bg_default
+                    )
+                )
+                .setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
+                .setTitle(R.string.nc_settings_remove_account)
+                .setMessage(R.string.nc_settings_remove_confirmation)
+                .setPositiveButton(R.string.nc_settings_remove, { removeCurrentAccount() })
+                .setNegativeButton(R.string.nc_cancel, null)
+                .setInstanceStateHandler(ID_REMOVE_ACCOUNT_WARNING_DIALOG, saveStateHandler)
+                .setSavedInstanceState(savedInstanceState)
+                .show()
+        }
+    }
+
+    private fun removeCurrentAccount() {
+        val otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser!!.id)
+        val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
+        WorkManager.getInstance().enqueue(accountRemovalWork)
+        if (otherUserExists && view != null) {
+            onViewBound((view)!!)
+            onAttach((view)!!)
+        } else if (!otherUserExists) {
+            router.setRoot(
+                RouterTransaction.with(ServerSelectionController())
+                    .pushChangeHandler(VerticalChangeHandler())
+                    .popChangeHandler(VerticalChangeHandler())
+            )
+        }
+    }
+
+    private fun getRingtoneName(context: Context, ringtoneUri: Uri?): String {
+        return if (ringtoneUri == null) {
+            resources!!.getString(R.string.nc_settings_no_ringtone)
+        } else if ((NotificationUtils.DEFAULT_CALL_RINGTONE_URI == ringtoneUri.toString()) ||
+            (NotificationUtils.DEFAULT_MESSAGE_RINGTONE_URI == ringtoneUri.toString())
+        ) {
+            resources!!.getString(R.string.nc_settings_default_ringtone)
+        } else {
+            val r = RingtoneManager.getRingtone(context, ringtoneUri)
+            r.getTitle(context)
+        }
+    }
+
+    override fun onAttach(view: View) {
+        super.onAttach(view)
+        actionBar?.show()
+        dispose(null)
+        getCurrentUser()
+
+        binding.settingsVersion.setOnClickListener {
+            sendLogs()
+        }
+
+        if (!TextUtils.isEmpty(currentUser!!.clientCertificate)) {
+            binding.settingsClientCert.setTitle(R.string.nc_client_cert_change)
+        } else {
+            binding.settingsClientCert.setTitle(R.string.nc_client_cert_setup)
+        }
+
+        setupCheckables()
+        setupScreenLockSetting()
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            binding.settingsNotificationsCategory.setTitle(
+                resources!!.getString(R.string.nc_settings_notification_sounds_post_oreo)
+            )
+        }
+
+        val callRingtoneUri = getCallRingtoneUri(view.context, (appPreferences)!!)
+        binding.settingsCallSound.setSummary(getRingtoneName(view.context, callRingtoneUri))
+        val messageRingtoneUri = getMessageRingtoneUri(view.context, (appPreferences)!!)
+        binding.settingsMessageSound.setSummary(getRingtoneName(view.context, messageRingtoneUri))
+
+        setupProxyTypeSettings()
+        setupProxyCredentialSettings()
+
+        if (currentUser != null) {
+            binding.baseUrlText.text = Uri.parse(currentUser!!.baseUrl).host
+            setupServerAgeWarning()
+
+            binding.settingsReauthorize.addPreferenceClickListener {
+                router.pushController(
+                    RouterTransaction.with(WebViewLoginController(currentUser!!.baseUrl, true))
+                        .pushChangeHandler(VerticalChangeHandler())
+                        .popChangeHandler(VerticalChangeHandler())
+                )
+            }
+
+            if (currentUser!!.displayName != null) {
+                binding.displayNameText.text = currentUser!!.displayName
+            }
+            DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false)
+
+            setupProfileQueryDisposable()
+
+            binding.settingsRemoveAccount.addPreferenceClickListener {
+                showLovelyDialog(ID_REMOVE_ACCOUNT_WARNING_DIALOG, null)
+            }
+        }
+        setupMessageView()
+
+        binding.avatarContainer.setOnClickListener {
+            router
+                .pushController(
+                    RouterTransaction.with(ProfileController())
+                        .pushChangeHandler(HorizontalChangeHandler())
+                        .popChangeHandler(HorizontalChangeHandler())
+
+                )
+        }
+    }
+
+    private fun setupProxyTypeSettings() {
+        if (("No proxy" == appPreferences!!.proxyType) || appPreferences!!.proxyType == null) {
+            hideProxySettings()
+        } else {
+            showProxySettings()
+        }
+    }
+
+    private fun setupProxyCredentialSettings() {
+        if (appPreferences!!.proxyCredentials) {
+            showProxyCredentials()
+        } else {
+            hideProxyCredentials()
+        }
+    }
+
+    private fun setupMessageView() {
+        if (ApplicationWideMessageHolder.getInstance().messageType != null) {
+            when (ApplicationWideMessageHolder.getInstance().messageType) {
+                ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED -> {
+                    binding.messageText.setTextColor(resources!!.getColor(R.color.colorPrimary))
+                    binding.messageText.text = resources!!.getString(R.string.nc_settings_account_updated)
+                    binding.messageView.visibility = View.VISIBLE
+                }
+
+                ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK -> {
+                    binding.messageText.setTextColor(resources!!.getColor(R.color.nc_darkRed))
+                    binding.messageText.text = resources!!.getString(R.string.nc_settings_wrong_account)
+                    binding.messageView.visibility = View.VISIBLE
+                    binding.messageText.setTextColor(resources!!.getColor(R.color.colorPrimary))
+                    binding.messageText.text = resources!!.getString(R.string.nc_Server_account_imported)
+                    binding.messageView.visibility = View.VISIBLE
+                }
+
+                ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED -> {
+                    binding.messageText.setTextColor(resources!!.getColor(R.color.colorPrimary))
+                    binding.messageText.text = resources!!.getString(R.string.nc_Server_account_imported)
+                    binding.messageView.visibility = View.VISIBLE
+                }
+
+                ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT -> {
+                    binding.messageText.setTextColor(resources!!.getColor(R.color.nc_darkRed))
+                    binding.messageText.text = resources!!.getString(R.string.nc_server_failed_to_import_account)
+                    binding.messageView.visibility = View.VISIBLE
+                }
+
+                else -> binding.messageView.visibility = View.GONE
+            }
+            ApplicationWideMessageHolder.getInstance().setMessageType(null)
+            binding.messageView.animate()
+                .translationY(0f)
+                .alpha(0.0f)
+                .setDuration(DURATION)
+                .setStartDelay(START_DELAY)
+                .setListener(object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        super.onAnimationEnd(animation)
+                        binding.messageView.visibility = View.GONE
+                    }
+                })
+        } else {
+            binding.messageView.visibility = View.GONE
+        }
+    }
+
+    private fun setupProfileQueryDisposable() {
+        profileQueryDisposable = ncApi.getUserProfile(
+            credentials,
+            ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)
+        )
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(
+                { userProfileOverall: UserProfileOverall ->
+                    var displayName: String? = null
+                    if (!TextUtils.isEmpty(
+                            userProfileOverall.ocs!!.data!!.displayName
+                        )
+                    ) {
+                        displayName = userProfileOverall.ocs!!.data!!.displayName
+                    } else if (!TextUtils.isEmpty(
+                            userProfileOverall.ocs!!.data!!.displayNameAlt
+                        )
+                    ) {
+                        displayName = userProfileOverall.ocs!!.data!!.displayNameAlt
+                    }
+                    if ((!TextUtils.isEmpty(displayName) && !(displayName == currentUser!!.displayName))) {
+                        dbQueryDisposable = userUtils.createOrUpdateUser(
+                            null,
+                            null,
+                            null,
+                            displayName,
+                            null,
+                            null,
+                            null,
+                            currentUser!!.id,
+                            null,
+                            null,
+                            null
+                        )
+                            .subscribeOn(Schedulers.io())
+                            .observeOn(AndroidSchedulers.mainThread())
+                            .subscribe(
+                                { userEntityResult: UserEntity ->
+                                    binding.displayNameText.text = userEntityResult.displayName
+                                },
+                                { dispose(dbQueryDisposable) },
+                                { dispose(dbQueryDisposable) }
+                            )
+                    }
+                },
+                { dispose(profileQueryDisposable) },
+                { dispose(profileQueryDisposable) }
+            )
+    }
+
+    private fun setupServerAgeWarning() {
+        when {
+            CapabilitiesUtil.isServerEOL(currentUser) -> {
+                binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context)!!, R.color.nc_darkRed))
+                binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol)
+                binding.serverAgeWarningIcon.setColorFilter(
+                    ContextCompat.getColor((context)!!, R.color.nc_darkRed),
+                    PorterDuff.Mode.SRC_IN
+                )
+            }
+            CapabilitiesUtil.isServerAlmostEOL(currentUser) -> {
+                binding.serverAgeWarningText.setTextColor(
+                    ContextCompat.getColor((context)!!, R.color.nc_darkYellow)
+                )
+                binding.serverAgeWarningText.setText(R.string.nc_settings_server_almost_eol)
+                binding.serverAgeWarningIcon.setColorFilter(
+                    ContextCompat.getColor((context)!!, R.color.nc_darkYellow),
+                    PorterDuff.Mode.SRC_IN
+                )
+            }
+            else -> {
+                binding.serverAgeWarningTextCard.visibility = View.GONE
+            }
+        }
+    }
+
+    private fun setupCheckables() {
+        (binding.settingsScreenSecurity.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+            appPreferences!!.isScreenSecured
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            (binding.settingsIncognitoKeyboard.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                appPreferences!!.isKeyboardIncognito
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            (binding.settingsIncognitoKeyboard.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                appPreferences!!.isKeyboardIncognito
+        }
+
+        if (CapabilitiesUtil.isReadStatusAvailable(userUtils.currentUser)) {
+            (binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                !CapabilitiesUtil.isReadStatusPrivate(currentUser)
+        } else {
+            binding.settingsReadPrivacy.visibility = View.GONE
+        }
+
+        (binding.settingsPhoneBookIntegration.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+            appPreferences!!.isPhoneBookIntegrationEnabled
+    }
+
+    private fun setupScreenLockSetting() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            val keyguardManager = context!!.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
+            if (keyguardManager.isKeyguardSecure) {
+                binding.settingsScreenLock.isEnabled = true
+                binding.settingsScreenLockTimeout.isEnabled = true
+                (binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                    appPreferences!!.isScreenLocked
+                binding.settingsScreenLockTimeout.isEnabled = appPreferences!!.isScreenLocked
+                if (appPreferences!!.isScreenLocked) {
+                    binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
+                } else {
+                    binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
+                }
+                binding.settingsScreenLock.alpha = ENABLED_ALPHA
+            } else {
+                binding.settingsScreenLock.isEnabled = false
+                binding.settingsScreenLockTimeout.isEnabled = false
+                appPreferences!!.removeScreenLock()
+                appPreferences!!.removeScreenLockTimeout()
+                (binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked = false
+                binding.settingsScreenLock.alpha = DISABLED_ALPHA
+                binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
+            }
+        }
+    }
+
+    public override fun onDestroy() {
+        appPreferences?.unregisterProxyTypeListener(proxyTypeChangeListener)
+        appPreferences?.unregisterProxyCredentialsListener(proxyCredentialsChangeListener)
+        appPreferences?.unregisterScreenSecurityListener(screenSecurityChangeListener)
+        appPreferences?.unregisterScreenLockListener(screenLockChangeListener)
+        appPreferences?.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener)
+        appPreferences?.unregisterThemeChangeListener(themeChangeListener)
+        appPreferences?.unregisterReadPrivacyChangeListener(readPrivacyChangeListener)
+        appPreferences?.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener)
+
+        super.onDestroy()
+    }
+
+    private fun hideProxySettings() {
+        appPreferences?.removeProxyHost()
+        appPreferences?.removeProxyPort()
+        appPreferences?.removeProxyCredentials()
+        appPreferences?.removeProxyUsername()
+        appPreferences?.removeProxyPassword()
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_host_edit).visibility = View.GONE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_port_edit).visibility = View.GONE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_use_credentials).visibility =
+            View.GONE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_username_edit).visibility = View.GONE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_password_edit).visibility = View.GONE
+    }
+
+    private fun showProxySettings() {
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_host_edit).visibility =
+            View.VISIBLE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_port_edit).visibility =
+            View.VISIBLE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_use_credentials).visibility =
+            View.VISIBLE
+    }
+
+    private fun showProxyCredentials() {
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_username_edit).visibility =
+            View.VISIBLE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_password_edit).visibility =
+            View.VISIBLE
+    }
+
+    private fun hideProxyCredentials() {
+        appPreferences?.removeProxyUsername()
+        appPreferences?.removeProxyPassword()
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_username_edit).visibility = View.GONE
+        binding.settingsScreen.findViewById<View>(R.id.settings_proxy_password_edit).visibility = View.GONE
+    }
+
+    private fun dispose(disposable: Disposable?) {
+        if (disposable != null && !disposable.isDisposed) {
+            disposable.dispose()
+        } else if (disposable == null) {
+            disposeProfileQueryDisposable()
+            disposeDbQueryDisposable()
+        }
+    }
+
+    private fun disposeDbQueryDisposable() {
+        if (dbQueryDisposable != null && !dbQueryDisposable!!.isDisposed) {
+            dbQueryDisposable!!.dispose()
+            dbQueryDisposable = null
+        } else if (dbQueryDisposable != null) {
+            dbQueryDisposable = null
+        }
+    }
+
+    private fun disposeProfileQueryDisposable() {
+        if (profileQueryDisposable != null && !profileQueryDisposable!!.isDisposed) {
+            profileQueryDisposable!!.dispose()
+            profileQueryDisposable = null
+        } else if (profileQueryDisposable != null) {
+            profileQueryDisposable = null
+        }
+    }
+
+    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
+        if (requestCode == ContactAddressBookWorker.REQUEST_PERMISSION &&
+            grantResults.isNotEmpty() &&
+            grantResults[0] == PackageManager.PERMISSION_GRANTED
+        ) {
+            WorkManager
+                .getInstance()
+                .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java).build())
+            checkForPhoneNumber()
+        } else {
+            appPreferences!!.setPhoneBookIntegration(false)
+            (binding.settingsPhoneBookIntegration.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                appPreferences!!.isPhoneBookIntegrationEnabled
+            Toast.makeText(
+                context,
+                context!!.resources.getString(R.string.no_phone_book_integration_due_to_permissions),
+                Toast.LENGTH_LONG
+            ).show()
+        }
+    }
+
+    private inner class ScreenLockTimeoutListener : OnPreferenceValueChangedListener<String?> {
+        override fun onChanged(newValue: String?) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                SecurityUtils.createKey(appPreferences!!.screenLockTimeout)
+            }
+        }
+    }
+
+    private inner class ScreenLockListener : OnPreferenceValueChangedListener<Boolean> {
+        override fun onChanged(newValue: Boolean) {
+            binding.settingsScreenLockTimeout.isEnabled = newValue
+            if (newValue) {
+                binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
+            } else {
+                binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
+            }
+        }
+    }
+
+    private inner class ScreenSecurityChangeListener : OnPreferenceValueChangedListener<Boolean> {
+        override fun onChanged(newValue: Boolean) {
+            if (newValue) {
+                if (activity != null) {
+                    activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+                }
+            } else {
+                if (activity != null) {
+                    activity!!.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
+                }
+            }
+        }
+    }
+
+    private inner class ProxyCredentialsChangeListener : OnPreferenceValueChangedListener<Boolean> {
+        override fun onChanged(newValue: Boolean) {
+            if (newValue) {
+                showProxyCredentials()
+            } else {
+                hideProxyCredentials()
+            }
+        }
+    }
+
+    private inner class ProxyTypeChangeListener : OnPreferenceValueChangedListener<String> {
+        @Suppress("Detekt.TooGenericExceptionCaught")
+        override fun onChanged(newValue: String) {
+            try {
+                if (("No proxy" == newValue)) {
+                    hideProxySettings()
+                } else {
+                    when (newValue) {
+                        "HTTP" ->
+                            binding.settingsProxyPortEdit.value = "3128"
+                        "DIRECT" ->
+                            binding.settingsProxyPortEdit.value = "8080"
+                        "SOCKS" ->
+                            binding.settingsProxyPortEdit.value = "1080"
+                        else -> {
+                        }
+                    }
+                    showProxySettings()
+                }
+            } catch (npe: NullPointerException) {
+                // view binding can be null
+                // since this is called asynchronously and UI might have been destroyed in the meantime
+                Log.i(TAG, "UI destroyed - view binding already gone")
+            }
+        }
+    }
+
+    private inner class ThemeChangeListener : OnPreferenceValueChangedListener<String?> {
+        override fun onChanged(newValue: String?) {
+            setAppTheme((newValue)!!)
+        }
+    }
+
+    private inner class PhoneBookIntegrationChangeListener(private val controller: Controller) :
+        OnPreferenceValueChangedListener<Boolean> {
+        override fun onChanged(isEnabled: Boolean) {
+            if (isEnabled) {
+                if (checkPermission(controller, (context)!!)) {
+                    checkForPhoneNumber()
+                }
+            } else {
+                deleteAll()
+            }
+        }
+    }
+
+    private fun checkForPhoneNumber() {
+        ncApi.getUserData(
+            ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
+            ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)
+        ).subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(object : Observer<UserProfileOverall> {
+                override fun onSubscribe(d: Disposable) {
+                    // unused atm
+                }
+
+                override fun onNext(userProfileOverall: UserProfileOverall) {
+                    if (userProfileOverall.ocs!!.data!!.phone?.isEmpty() == true) {
+                        askForPhoneNumber()
+                    } else {
+                        Log.d(TAG, "phone number already set")
+                    }
+                }
+
+                override fun onError(e: Throwable) {
+                    // unused atm
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+            })
+    }
+
+    private fun askForPhoneNumber() {
+        val phoneNumberLayoutWrapper = LinearLayout(activity)
+        phoneNumberLayoutWrapper.orientation = LinearLayout.VERTICAL
+        phoneNumberLayoutWrapper.setPadding(PHONE_NUMBER_SIDE_PADDING, 0, PHONE_NUMBER_SIDE_PADDING, 0)
+        val phoneNumberInputLayout = TextInputLayout((activity)!!)
+        val phoneNumberField = EditText(activity)
+        phoneNumberInputLayout.setHelperTextColor(ColorStateList.valueOf(resources!!.getColor(R.color.nc_darkRed)))
+        phoneNumberField.inputType = InputType.TYPE_CLASS_PHONE
+        phoneNumberField.setText("+")
+        phoneNumberField.addTextChangedListener(object : TextWatcher {
+            override fun afterTextChanged(s: Editable) {
+                // unused atm
+            }
+
+            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+                // unused atm
+            }
+
+            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                phoneNumberInputLayout.helperText = ""
+            }
+        })
+        phoneNumberInputLayout.addView(phoneNumberField)
+        phoneNumberLayoutWrapper.addView(phoneNumberInputLayout)
+        val dialog = AlertDialog.Builder((activity)!!)
+            .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title)
+            .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description)
+            .setView(phoneNumberLayoutWrapper)
+            .setPositiveButton(context!!.resources.getString(R.string.nc_common_set), null)
+            .setNegativeButton(context!!.resources.getString(R.string.nc_common_skip), null)
+            .create()
+        dialog.setOnShowListener(object : OnShowListener {
+            override fun onShow(dialogInterface: DialogInterface) {
+                val button = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
+                button.setOnClickListener(object : View.OnClickListener {
+                    override fun onClick(view: View) {
+                        setPhoneNumber(phoneNumberInputLayout, dialog)
+                    }
+                })
+            }
+        })
+        dialog.show()
+    }
+
+    private fun setPhoneNumber(textInputLayout: TextInputLayout, dialog: AlertDialog) {
+        val phoneNumber = textInputLayout.editText!!.text.toString()
+        ncApi.setUserData(
+            ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
+            ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), "phone", phoneNumber
+        ).subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(object : Observer<GenericOverall> {
+                override fun onSubscribe(d: Disposable) {
+                    // unused atm
+                }
+
+                override fun onNext(genericOverall: GenericOverall) {
+                    val statusCode = genericOverall.meta?.statusCode
+                    if (statusCode == HTTP_CODE) {
+                        dialog.dismiss()
+                        Toast.makeText(
+                            context,
+                            context!!.resources.getString(
+                                R.string.nc_settings_phone_book_integration_phone_number_dialog_success
+                            ),
+                            Toast.LENGTH_LONG
+                        ).show()
+                    } else {
+                        textInputLayout.helperText = context!!.resources.getString(
+                            R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid
+                        )
+                        Log.d(TAG, "failed to set phoneNumber. statusCode=$statusCode")
+                    }
+                }
+
+                override fun onError(e: Throwable) {
+                    textInputLayout.helperText = context!!.resources.getString(
+                        R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid
+                    )
+                    Log.e(TAG, "setPhoneNumber error", e)
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+            })
+    }
+
+    private inner class ReadPrivacyChangeListener : OnPreferenceValueChangedListener<Boolean> {
+        override fun onChanged(newValue: Boolean) {
+            val booleanValue = if (newValue) "0" else "1"
+            val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}"
+            ncApi.setReadStatusPrivacy(
+                ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
+                ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl),
+                RequestBody.create("application/json".toMediaTypeOrNull(), json)
+            )
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(object : Observer<GenericOverall> {
+                    override fun onSubscribe(d: Disposable) {
+                        // unused atm
+                    }
+
+                    override fun onNext(genericOverall: GenericOverall) {
+                        // unused atm
+                    }
+
+                    override fun onError(e: Throwable) {
+                        appPreferences!!.setReadPrivacy(!newValue)
+                        (binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
+                            !newValue
+                    }
+
+                    override fun onComplete() {
+                        // unused atm
+                    }
+                })
+        }
+    }
+
+    companion object {
+        private const val TAG = "SettingsController"
+        private const val ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0
+        private const val DURATION: Long = 2500
+        private const val START_DELAY: Long = 5000
+        private const val DISABLED_ALPHA: Float = 0.38f
+        private const val ENABLED_ALPHA: Float = 1.0f
+        private const val HTTP_CODE: Int = 200
+        private const val PHONE_NUMBER_SIDE_PADDING: Int = 50
+    }
+}

+ 1 - 5
app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOverall.kt

@@ -30,11 +30,7 @@ import kotlinx.android.parcel.Parcelize
 data class GenericOverall(
     @JsonField(name = ["meta"])
     var meta: GenericMeta? = null
-) : IGenericOCS, Parcelable {
+) : Parcelable {
     // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
     constructor() : this(null)
-
-    override fun getGenericMeta(): GenericMeta? {
-        return meta
-    }
 }

+ 1 - 1
detekt.yml

@@ -1,5 +1,5 @@
 build:
-  maxIssues: 91
+  maxIssues: 89
   weights:
     # complexity: 2
     # LongParameterList: 1