SettingsController.java 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. /*
  2. * Nextcloud Talk application
  3. *
  4. * @author Andy Scherzinger
  5. * @author Mario Danic
  6. * @author Tim Krüger
  7. * Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
  8. * Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
  9. * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. package com.nextcloud.talk.controllers;
  25. import android.animation.Animator;
  26. import android.animation.AnimatorListenerAdapter;
  27. import android.app.KeyguardManager;
  28. import android.content.Context;
  29. import android.content.DialogInterface;
  30. import android.content.Intent;
  31. import android.content.pm.PackageManager;
  32. import android.content.res.ColorStateList;
  33. import android.graphics.PorterDuff;
  34. import android.net.Uri;
  35. import android.os.Build;
  36. import android.os.Bundle;
  37. import android.security.KeyChain;
  38. import android.text.Editable;
  39. import android.text.InputType;
  40. import android.text.TextUtils;
  41. import android.text.TextWatcher;
  42. import android.util.Log;
  43. import android.view.LayoutInflater;
  44. import android.view.View;
  45. import android.view.ViewGroup;
  46. import android.view.WindowManager;
  47. import android.widget.Button;
  48. import android.widget.Checkable;
  49. import android.widget.EditText;
  50. import android.widget.ImageView;
  51. import android.widget.LinearLayout;
  52. import android.widget.RelativeLayout;
  53. import android.widget.TextView;
  54. import android.widget.Toast;
  55. import com.bluelinelabs.conductor.Controller;
  56. import com.bluelinelabs.conductor.RouterTransaction;
  57. import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
  58. import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
  59. import com.bluelinelabs.logansquare.LoganSquare;
  60. import com.facebook.drawee.view.SimpleDraweeView;
  61. import com.google.android.material.card.MaterialCardView;
  62. import com.google.android.material.textfield.TextInputLayout;
  63. import com.nextcloud.talk.BuildConfig;
  64. import com.nextcloud.talk.R;
  65. import com.nextcloud.talk.api.NcApi;
  66. import com.nextcloud.talk.application.NextcloudTalkApplication;
  67. import com.nextcloud.talk.controllers.base.BaseController;
  68. import com.nextcloud.talk.jobs.AccountRemovalWorker;
  69. import com.nextcloud.talk.jobs.ContactAddressBookWorker;
  70. import com.nextcloud.talk.models.RingtoneSettings;
  71. import com.nextcloud.talk.models.database.CapabilitiesUtil;
  72. import com.nextcloud.talk.models.database.UserEntity;
  73. import com.nextcloud.talk.models.json.generic.GenericOverall;
  74. import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
  75. import com.nextcloud.talk.utils.ApiUtils;
  76. import com.nextcloud.talk.utils.DisplayUtils;
  77. import com.nextcloud.talk.utils.DoNotDisturbUtils;
  78. import com.nextcloud.talk.utils.LoggingUtils;
  79. import com.nextcloud.talk.utils.SecurityUtils;
  80. import com.nextcloud.talk.utils.bundle.BundleKeys;
  81. import com.nextcloud.talk.utils.database.user.UserUtils;
  82. import com.nextcloud.talk.utils.preferences.AppPreferences;
  83. import com.nextcloud.talk.utils.preferences.MagicUserInputModule;
  84. import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder;
  85. import com.yarolegovich.lovelydialog.LovelySaveStateHandler;
  86. import com.yarolegovich.lovelydialog.LovelyStandardDialog;
  87. import com.yarolegovich.mp.MaterialChoicePreference;
  88. import com.yarolegovich.mp.MaterialEditTextPreference;
  89. import com.yarolegovich.mp.MaterialPreferenceCategory;
  90. import com.yarolegovich.mp.MaterialPreferenceScreen;
  91. import com.yarolegovich.mp.MaterialStandardPreference;
  92. import com.yarolegovich.mp.MaterialSwitchPreference;
  93. import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener;
  94. import java.io.IOException;
  95. import java.net.URI;
  96. import java.net.URISyntaxException;
  97. import java.util.ArrayList;
  98. import java.util.Arrays;
  99. import java.util.List;
  100. import java.util.Locale;
  101. import java.util.Objects;
  102. import javax.inject.Inject;
  103. import androidx.annotation.NonNull;
  104. import androidx.annotation.Nullable;
  105. import androidx.appcompat.app.AlertDialog;
  106. import androidx.core.content.ContextCompat;
  107. import androidx.core.view.ViewCompat;
  108. import androidx.emoji.widget.EmojiTextView;
  109. import androidx.work.OneTimeWorkRequest;
  110. import androidx.work.WorkManager;
  111. import autodagger.AutoInjector;
  112. import butterknife.BindView;
  113. import butterknife.OnClick;
  114. import io.reactivex.Observer;
  115. import io.reactivex.android.schedulers.AndroidSchedulers;
  116. import io.reactivex.disposables.Disposable;
  117. import io.reactivex.schedulers.Schedulers;
  118. import okhttp3.MediaType;
  119. import okhttp3.RequestBody;
  120. @AutoInjector(NextcloudTalkApplication.class)
  121. public class SettingsController extends BaseController {
  122. public static final String TAG = "SettingsController";
  123. private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0;
  124. @BindView(R.id.settings_screen)
  125. MaterialPreferenceScreen settingsScreen;
  126. @BindView(R.id.settings_proxy_choice)
  127. MaterialChoicePreference proxyChoice;
  128. @BindView(R.id.settings_proxy_port_edit)
  129. MaterialEditTextPreference proxyPortEditText;
  130. @BindView(R.id.settings_licence)
  131. MaterialStandardPreference licenceButton;
  132. @BindView(R.id.settings_privacy)
  133. MaterialStandardPreference privacyButton;
  134. @BindView(R.id.settings_source_code)
  135. MaterialStandardPreference sourceCodeButton;
  136. @BindView(R.id.settings_version)
  137. MaterialStandardPreference versionInfo;
  138. @BindView(R.id.avatarContainer)
  139. RelativeLayout avatarContainer;
  140. @BindView(R.id.avatar_image)
  141. SimpleDraweeView avatarImageView;
  142. @BindView(R.id.display_name_text)
  143. EmojiTextView displayNameTextView;
  144. @BindView(R.id.base_url_text)
  145. TextView baseUrlTextView;
  146. @BindView(R.id.server_age_warning_text_card)
  147. MaterialCardView serverAgeCardView;
  148. @BindView(R.id.server_age_warning_text)
  149. TextView serverAgeTextView;
  150. @BindView(R.id.server_age_warning_icon)
  151. ImageView serverAgeIcon;
  152. @BindView(R.id.settings_call_sound)
  153. MaterialStandardPreference settingsCallSound;
  154. @BindView(R.id.settings_message_sound)
  155. MaterialStandardPreference settingsMessageSound;
  156. @BindView(R.id.settings_remove_account)
  157. MaterialStandardPreference removeAccountButton;
  158. @BindView(R.id.settings_reauthorize)
  159. MaterialStandardPreference reauthorizeButton;
  160. @BindView(R.id.message_view)
  161. MaterialPreferenceCategory messageView;
  162. @BindView(R.id.settings_client_cert)
  163. MaterialStandardPreference certificateSetup;
  164. @BindView(R.id.settings_incognito_keyboard)
  165. MaterialSwitchPreference incognitoKeyboardSwitchPreference;
  166. @BindView(R.id.settings_screen_security)
  167. MaterialSwitchPreference screenSecuritySwitchPreference;
  168. @BindView(R.id.settings_screen_lock)
  169. MaterialSwitchPreference screenLockSwitchPreference;
  170. @BindView(R.id.settings_screen_lock_timeout)
  171. MaterialChoicePreference screenLockTimeoutChoicePreference;
  172. @BindView(R.id.settings_phone_book_integration)
  173. MaterialSwitchPreference phoneBookIntegrationPreference;
  174. @BindView(R.id.settings_read_privacy)
  175. MaterialSwitchPreference readPrivacyPreference;
  176. @BindView(R.id.message_text)
  177. TextView messageText;
  178. @Inject
  179. AppPreferences appPreferences;
  180. @Inject
  181. NcApi ncApi;
  182. @Inject
  183. UserUtils userUtils;
  184. @Inject
  185. Context context;
  186. private LovelySaveStateHandler saveStateHandler;
  187. private UserEntity currentUser;
  188. private String credentials;
  189. private OnPreferenceValueChangedListener<String> proxyTypeChangeListener;
  190. private OnPreferenceValueChangedListener<Boolean> proxyCredentialsChangeListener;
  191. private OnPreferenceValueChangedListener<Boolean> screenSecurityChangeListener;
  192. private OnPreferenceValueChangedListener<Boolean> screenLockChangeListener;
  193. private OnPreferenceValueChangedListener<String> screenLockTimeoutChangeListener;
  194. private OnPreferenceValueChangedListener<String> themeChangeListener;
  195. private OnPreferenceValueChangedListener<Boolean> readPrivacyChangeListener;
  196. private OnPreferenceValueChangedListener<Boolean> phoneBookIntegrationChangeListener;
  197. private Disposable profileQueryDisposable;
  198. private Disposable dbQueryDisposable;
  199. @NonNull
  200. @Override
  201. protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
  202. return inflater.inflate(R.layout.controller_settings, container, false);
  203. }
  204. private void getCurrentUser() {
  205. currentUser = userUtils.getCurrentUser();
  206. credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken());
  207. }
  208. @Override
  209. protected void onViewBound(@NonNull View view) {
  210. super.onViewBound(view);
  211. setHasOptionsMenu(true);
  212. ViewCompat.setTransitionName(avatarImageView, "userAvatar.transitionTag");
  213. NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
  214. getCurrentUser();
  215. if (saveStateHandler == null) {
  216. saveStateHandler = new LovelySaveStateHandler();
  217. }
  218. appPreferences.registerProxyTypeListener(proxyTypeChangeListener = new ProxyTypeChangeListener());
  219. appPreferences.registerProxyCredentialsListener(proxyCredentialsChangeListener = new ProxyCredentialsChangeListener());
  220. appPreferences.registerScreenSecurityListener(screenSecurityChangeListener = new ScreenSecurityChangeListener());
  221. appPreferences.registerScreenLockListener(screenLockChangeListener = new ScreenLockListener());
  222. appPreferences.registerScreenLockTimeoutListener(screenLockTimeoutChangeListener = new ScreenLockTimeoutListener());
  223. appPreferences.registerThemeChangeListener(themeChangeListener = new ThemeChangeListener());
  224. appPreferences.registerPhoneBookIntegrationChangeListener(
  225. phoneBookIntegrationChangeListener = new PhoneBookIntegrationChangeListener(this));
  226. appPreferences.registerReadPrivacyChangeListener(readPrivacyChangeListener = new ReadPrivacyChangeListener());
  227. List<String> listWithIntFields = new ArrayList<>();
  228. listWithIntFields.add("proxy_port");
  229. settingsScreen.setUserInputModule(new MagicUserInputModule(getActivity(), listWithIntFields));
  230. settingsScreen.setVisibilityController(R.id.settings_proxy_use_credentials,
  231. Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit),
  232. true);
  233. if (!TextUtils.isEmpty(getResources().getString(R.string.nc_gpl3_url))) {
  234. licenceButton.addPreferenceClickListener(view1 -> {
  235. Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().
  236. getString(R.string.nc_gpl3_url)));
  237. startActivity(browserIntent);
  238. });
  239. } else {
  240. licenceButton.setVisibility(View.GONE);
  241. }
  242. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
  243. incognitoKeyboardSwitchPreference.setVisibility(View.GONE);
  244. }
  245. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
  246. screenLockSwitchPreference.setVisibility(View.GONE);
  247. screenLockTimeoutChoicePreference.setVisibility(View.GONE);
  248. } else {
  249. screenLockSwitchPreference.setSummary(String.format(Locale.getDefault(),
  250. getResources().getString(R.string.nc_settings_screen_lock_desc),
  251. getResources().getString(R.string.nc_app_product_name)));
  252. }
  253. if (!TextUtils.isEmpty(getResources().getString(R.string.nc_privacy_url))) {
  254. privacyButton.addPreferenceClickListener(view12 -> {
  255. Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().
  256. getString(R.string.nc_privacy_url)));
  257. startActivity(browserIntent);
  258. });
  259. } else {
  260. privacyButton.setVisibility(View.GONE);
  261. }
  262. if (!TextUtils.isEmpty(getResources().getString(R.string.nc_source_code_url))) {
  263. sourceCodeButton.addPreferenceClickListener(view13 -> {
  264. Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().
  265. getString(R.string.nc_source_code_url)));
  266. startActivity(browserIntent);
  267. });
  268. } else {
  269. sourceCodeButton.setVisibility(View.GONE);
  270. }
  271. versionInfo.setSummary("v" + BuildConfig.VERSION_NAME);
  272. settingsCallSound.setOnClickListener(v -> {
  273. Bundle bundle = new Bundle();
  274. bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), true);
  275. getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle))
  276. .pushChangeHandler(new HorizontalChangeHandler())
  277. .popChangeHandler(new HorizontalChangeHandler()));
  278. });
  279. settingsMessageSound.setOnClickListener(v -> {
  280. Bundle bundle = new Bundle();
  281. bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false);
  282. getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle))
  283. .pushChangeHandler(new HorizontalChangeHandler())
  284. .popChangeHandler(new HorizontalChangeHandler()));
  285. });
  286. if (CapabilitiesUtil.isPhoneBookIntegrationAvailable(userUtils.getCurrentUser())) {
  287. phoneBookIntegrationPreference.setVisibility(View.VISIBLE);
  288. } else {
  289. phoneBookIntegrationPreference.setVisibility(View.GONE);
  290. }
  291. String host = null;
  292. int port = -1;
  293. URI uri;
  294. try {
  295. uri = new URI(currentUser.getBaseUrl());
  296. host = uri.getHost();
  297. port = uri.getPort();
  298. } catch (URISyntaxException e) {
  299. Log.e(TAG, "Failed to create uri");
  300. }
  301. String finalHost = host;
  302. int finalPort = port;
  303. certificateSetup.addPreferenceClickListener(v -> KeyChain.choosePrivateKeyAlias(Objects.requireNonNull(getActivity()), alias -> {
  304. String finalAlias = alias;
  305. getActivity().runOnUiThread(() -> {
  306. if (finalAlias != null) {
  307. certificateSetup.setTitle(R.string.nc_client_cert_change);
  308. } else {
  309. certificateSetup.setTitle(R.string.nc_client_cert_setup);
  310. }
  311. });
  312. if (alias == null) {
  313. alias = "";
  314. }
  315. userUtils.createOrUpdateUser(null, null, null, null, null, null, null, currentUser.getId(),
  316. null, alias, null);
  317. }, new String[]{"RSA", "EC"}, null, finalHost, finalPort, currentUser.getClientCertificate
  318. ()));
  319. }
  320. private void showLovelyDialog(int dialogId, Bundle savedInstanceState) {
  321. if (dialogId == ID_REMOVE_ACCOUNT_WARNING_DIALOG) {
  322. showRemoveAccountWarning(savedInstanceState);
  323. }
  324. }
  325. @OnClick(R.id.settings_version)
  326. void sendLogs() {
  327. if (getResources().getBoolean(R.bool.nc_is_debug)) {
  328. LoggingUtils.INSTANCE.sendMailWithAttachment(context);
  329. }
  330. }
  331. @Override
  332. protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
  333. saveStateHandler.saveInstanceState(outState);
  334. super.onSaveViewState(view, outState);
  335. }
  336. @Override
  337. protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
  338. super.onRestoreViewState(view, savedViewState);
  339. if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) {
  340. //Dialog won't be restarted automatically, so we need to call this method.
  341. //Each dialog knows how to restore its state
  342. showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState);
  343. }
  344. }
  345. private void showRemoveAccountWarning(Bundle savedInstanceState) {
  346. if (getActivity() != null) {
  347. new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL)
  348. .setTopColorRes(R.color.nc_darkRed)
  349. .setIcon(DisplayUtils.getTintedDrawable(getResources(),
  350. R.drawable.ic_delete_black_24dp, R.color.bg_default))
  351. .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed))
  352. .setTitle(R.string.nc_settings_remove_account)
  353. .setMessage(R.string.nc_settings_remove_confirmation)
  354. .setPositiveButton(R.string.nc_settings_remove, new View.OnClickListener() {
  355. @Override
  356. public void onClick(View v) {
  357. removeCurrentAccount();
  358. }
  359. })
  360. .setNegativeButton(R.string.nc_cancel, null)
  361. .setInstanceStateHandler(ID_REMOVE_ACCOUNT_WARNING_DIALOG, saveStateHandler)
  362. .setSavedInstanceState(savedInstanceState)
  363. .show();
  364. }
  365. }
  366. private void removeCurrentAccount() {
  367. boolean otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser.getId());
  368. OneTimeWorkRequest accountRemovalWork = new OneTimeWorkRequest.Builder(AccountRemovalWorker.class).build();
  369. WorkManager.getInstance().enqueue(accountRemovalWork);
  370. if (otherUserExists && getView() != null) {
  371. onViewBound(getView());
  372. onAttach(getView());
  373. } else if (!otherUserExists) {
  374. getRouter().setRoot(RouterTransaction.with(
  375. new ServerSelectionController())
  376. .pushChangeHandler(new VerticalChangeHandler())
  377. .popChangeHandler(new VerticalChangeHandler()));
  378. }
  379. }
  380. @Override
  381. protected void onAttach(@NonNull View view) {
  382. super.onAttach(view);
  383. if (getActionBar() != null) {
  384. getActionBar().show();
  385. }
  386. dispose(null);
  387. getCurrentUser();
  388. if (!TextUtils.isEmpty(currentUser.getClientCertificate())) {
  389. certificateSetup.setTitle(R.string.nc_client_cert_change);
  390. } else {
  391. certificateSetup.setTitle(R.string.nc_client_cert_setup);
  392. }
  393. ((Checkable) screenSecuritySwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenSecured());
  394. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  395. ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito());
  396. }
  397. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  398. ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito());
  399. }
  400. if (CapabilitiesUtil.isReadStatusAvailable(userUtils.getCurrentUser())) {
  401. ((Checkable) readPrivacyPreference.findViewById(R.id.mp_checkable)).setChecked(!CapabilitiesUtil.isReadStatusPrivate(currentUser));
  402. } else {
  403. readPrivacyPreference.setVisibility(View.GONE);
  404. }
  405. ((Checkable) phoneBookIntegrationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled());
  406. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  407. KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
  408. if (keyguardManager.isKeyguardSecure()) {
  409. screenLockSwitchPreference.setEnabled(true);
  410. screenLockTimeoutChoicePreference.setEnabled(true);
  411. ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenLocked());
  412. screenLockTimeoutChoicePreference.setEnabled(appPreferences.getIsScreenLocked());
  413. if (appPreferences.getIsScreenLocked()) {
  414. screenLockTimeoutChoicePreference.setAlpha(1.0f);
  415. } else {
  416. screenLockTimeoutChoicePreference.setAlpha(0.38f);
  417. }
  418. screenLockSwitchPreference.setAlpha(1.0f);
  419. } else {
  420. screenLockSwitchPreference.setEnabled(false);
  421. screenLockTimeoutChoicePreference.setEnabled(false);
  422. appPreferences.removeScreenLock();
  423. appPreferences.removeScreenLockTimeout();
  424. ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(false);
  425. screenLockSwitchPreference.setAlpha(0.38f);
  426. screenLockTimeoutChoicePreference.setAlpha(0.38f);
  427. }
  428. }
  429. String ringtoneName = "";
  430. RingtoneSettings ringtoneSettings;
  431. if (!TextUtils.isEmpty(appPreferences.getCallRingtoneUri())) {
  432. try {
  433. ringtoneSettings = LoganSquare.parse(appPreferences.getCallRingtoneUri(), RingtoneSettings.class);
  434. ringtoneName = ringtoneSettings.getRingtoneName();
  435. } catch (IOException e) {
  436. Log.e(TAG, "Failed to parse ringtone name");
  437. }
  438. settingsCallSound.setSummary(ringtoneName);
  439. } else {
  440. settingsCallSound.setSummary(R.string.nc_settings_default_ringtone);
  441. }
  442. ringtoneName = "";
  443. if (!TextUtils.isEmpty(appPreferences.getMessageRingtoneUri())) {
  444. try {
  445. ringtoneSettings = LoganSquare.parse(appPreferences.getMessageRingtoneUri(), RingtoneSettings.class);
  446. ringtoneName = ringtoneSettings.getRingtoneName();
  447. } catch (IOException e) {
  448. Log.e(TAG, "Failed to parse ringtone name");
  449. }
  450. settingsMessageSound.setSummary(ringtoneName);
  451. } else {
  452. settingsMessageSound.setSummary(R.string.nc_settings_default_ringtone);
  453. }
  454. if ("No proxy".equals(appPreferences.getProxyType()) || appPreferences.getProxyType() == null) {
  455. hideProxySettings();
  456. } else {
  457. showProxySettings();
  458. }
  459. if (appPreferences.getProxyCredentials()) {
  460. showProxyCredentials();
  461. } else {
  462. hideProxyCredentials();
  463. }
  464. if (currentUser != null) {
  465. baseUrlTextView.setText(Uri.parse(currentUser.getBaseUrl()).getHost());
  466. if (CapabilitiesUtil.isServerEOL(currentUser)) {
  467. serverAgeTextView.setTextColor(ContextCompat.getColor(context, R.color.nc_darkRed));
  468. serverAgeTextView.setText(R.string.nc_settings_server_eol);
  469. serverAgeIcon.setColorFilter(ContextCompat.getColor(context, R.color.nc_darkRed),
  470. PorterDuff.Mode.SRC_IN);
  471. } else if (CapabilitiesUtil.isServerAlmostEOL(currentUser)) {
  472. serverAgeTextView.setTextColor(ContextCompat.getColor(context, R.color.nc_darkYellow));
  473. serverAgeTextView.setText(R.string.nc_settings_server_almost_eol);
  474. serverAgeIcon.setColorFilter(ContextCompat.getColor(context, R.color.nc_darkYellow),
  475. PorterDuff.Mode.SRC_IN);
  476. } else {
  477. serverAgeCardView.setVisibility(View.GONE);
  478. }
  479. reauthorizeButton.addPreferenceClickListener(view14 -> {
  480. getRouter().pushController(RouterTransaction.with(
  481. new WebViewLoginController(currentUser.getBaseUrl(), true))
  482. .pushChangeHandler(new VerticalChangeHandler())
  483. .popChangeHandler(new VerticalChangeHandler()));
  484. });
  485. if (currentUser.getDisplayName() != null) {
  486. displayNameTextView.setText(currentUser.getDisplayName());
  487. }
  488. DisplayUtils.loadAvatarImage(currentUser, avatarImageView, false);
  489. profileQueryDisposable = ncApi.getUserProfile(credentials,
  490. ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()))
  491. .subscribeOn(Schedulers.io())
  492. .observeOn(AndroidSchedulers.mainThread())
  493. .subscribe(userProfileOverall -> {
  494. String displayName = null;
  495. if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData()
  496. .getDisplayName())) {
  497. displayName = userProfileOverall.getOcs().getData()
  498. .getDisplayName();
  499. } else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData()
  500. .getDisplayNameAlt())) {
  501. displayName = userProfileOverall.getOcs().getData()
  502. .getDisplayNameAlt();
  503. }
  504. if ((!TextUtils.isEmpty(displayName) && !displayName.equals(currentUser.getDisplayName()))) {
  505. dbQueryDisposable = userUtils.createOrUpdateUser(null,
  506. null,
  507. null, displayName, null, null,
  508. null, currentUser.getId(), null, null, null)
  509. .subscribeOn(Schedulers.io())
  510. .observeOn(AndroidSchedulers.mainThread())
  511. .subscribe(userEntityResult -> {
  512. displayNameTextView.setText(userEntityResult.getDisplayName());
  513. },
  514. throwable -> {
  515. dispose(dbQueryDisposable);
  516. }, () -> dispose(dbQueryDisposable));
  517. }
  518. }, throwable -> {
  519. dispose(profileQueryDisposable);
  520. }, () -> dispose(profileQueryDisposable));
  521. removeAccountButton.addPreferenceClickListener(view1 -> {
  522. showLovelyDialog(ID_REMOVE_ACCOUNT_WARNING_DIALOG, null);
  523. });
  524. }
  525. if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) {
  526. switch (ApplicationWideMessageHolder.getInstance().getMessageType()) {
  527. case ACCOUNT_UPDATED_NOT_ADDED:
  528. messageText.setTextColor(getResources().getColor(R.color.colorPrimary));
  529. messageText.setText(getResources().getString(R.string.nc_settings_account_updated));
  530. messageView.setVisibility(View.VISIBLE);
  531. break;
  532. case SERVER_WITHOUT_TALK:
  533. messageText.setTextColor(getResources().getColor(R.color.nc_darkRed));
  534. messageText.setText(getResources().getString(R.string.nc_settings_wrong_account));
  535. messageView.setVisibility(View.VISIBLE);
  536. case ACCOUNT_WAS_IMPORTED:
  537. messageText.setTextColor(getResources().getColor(R.color.colorPrimary));
  538. messageText.setText(getResources().getString(R.string.nc_Server_account_imported));
  539. messageView.setVisibility(View.VISIBLE);
  540. break;
  541. case FAILED_TO_IMPORT_ACCOUNT:
  542. messageText.setTextColor(getResources().getColor(R.color.nc_darkRed));
  543. messageText.setText(getResources().getString(R.string.nc_server_failed_to_import_account));
  544. messageView.setVisibility(View.VISIBLE);
  545. break;
  546. default:
  547. messageView.setVisibility(View.GONE);
  548. break;
  549. }
  550. ApplicationWideMessageHolder.getInstance().setMessageType(null);
  551. messageView.animate()
  552. .translationY(0)
  553. .alpha(0.0f)
  554. .setDuration(2500)
  555. .setStartDelay(5000)
  556. .setListener(new AnimatorListenerAdapter() {
  557. @Override
  558. public void onAnimationEnd(Animator animation) {
  559. super.onAnimationEnd(animation);
  560. if (messageView != null) {
  561. messageView.setVisibility(View.GONE);
  562. }
  563. }
  564. });
  565. } else {
  566. if (messageView != null) {
  567. messageView.setVisibility(View.GONE);
  568. }
  569. }
  570. avatarContainer.setOnClickListener(v ->
  571. getRouter()
  572. .pushController((RouterTransaction.with(new ProfileController())
  573. .pushChangeHandler(new HorizontalChangeHandler())
  574. .popChangeHandler(new HorizontalChangeHandler()))));
  575. }
  576. @Override
  577. public void onDestroy() {
  578. if (appPreferences != null) {
  579. appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener);
  580. appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener);
  581. appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener);
  582. appPreferences.unregisterScreenLockListener(screenLockChangeListener);
  583. appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener);
  584. appPreferences.unregisterThemeChangeListener(themeChangeListener);
  585. appPreferences.unregisterReadPrivacyChangeListener(readPrivacyChangeListener);
  586. appPreferences.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener);
  587. }
  588. super.onDestroy();
  589. }
  590. private void hideProxySettings() {
  591. appPreferences.removeProxyHost();
  592. appPreferences.removeProxyPort();
  593. appPreferences.removeProxyCredentials();
  594. appPreferences.removeProxyUsername();
  595. appPreferences.removeProxyPassword();
  596. settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.GONE);
  597. settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.GONE);
  598. settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.GONE);
  599. settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE);
  600. settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE);
  601. }
  602. private void showProxySettings() {
  603. settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.VISIBLE);
  604. settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.VISIBLE);
  605. settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.VISIBLE);
  606. }
  607. private void showProxyCredentials() {
  608. settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.VISIBLE);
  609. settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.VISIBLE);
  610. }
  611. private void hideProxyCredentials() {
  612. appPreferences.removeProxyUsername();
  613. appPreferences.removeProxyPassword();
  614. settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE);
  615. settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE);
  616. }
  617. private void dispose(@Nullable Disposable disposable) {
  618. if (disposable != null && !disposable.isDisposed()) {
  619. disposable.dispose();
  620. } else if (disposable == null) {
  621. if (profileQueryDisposable != null && !profileQueryDisposable.isDisposed()) {
  622. profileQueryDisposable.dispose();
  623. profileQueryDisposable = null;
  624. } else if (profileQueryDisposable != null) {
  625. profileQueryDisposable = null;
  626. }
  627. if (dbQueryDisposable != null && !dbQueryDisposable.isDisposed()) {
  628. dbQueryDisposable.dispose();
  629. dbQueryDisposable = null;
  630. } else if (dbQueryDisposable != null) {
  631. dbQueryDisposable = null;
  632. }
  633. }
  634. }
  635. @Override
  636. protected String getTitle() {
  637. return getResources().getString(R.string.nc_settings);
  638. }
  639. @Override
  640. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  641. if (requestCode == ContactAddressBookWorker.REQUEST_PERMISSION &&
  642. grantResults.length > 0 &&
  643. grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  644. WorkManager
  645. .getInstance()
  646. .enqueue(new OneTimeWorkRequest.Builder(ContactAddressBookWorker.class).build());
  647. checkForPhoneNumber();
  648. } else {
  649. appPreferences.setPhoneBookIntegration(false);
  650. ((Checkable) phoneBookIntegrationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled());
  651. Toast.makeText(context, context.getResources().getString(
  652. R.string.no_phone_book_integration_due_to_permissions),
  653. Toast.LENGTH_LONG).show();
  654. }
  655. }
  656. private class ScreenLockTimeoutListener implements OnPreferenceValueChangedListener<String> {
  657. @Override
  658. public void onChanged(String newValue) {
  659. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  660. SecurityUtils.createKey(appPreferences.getScreenLockTimeout());
  661. }
  662. }
  663. }
  664. private class ScreenLockListener implements OnPreferenceValueChangedListener<Boolean> {
  665. @Override
  666. public void onChanged(Boolean newValue) {
  667. screenLockTimeoutChoicePreference.setEnabled(newValue);
  668. if (newValue) {
  669. screenLockTimeoutChoicePreference.setAlpha(1.0f);
  670. } else {
  671. screenLockTimeoutChoicePreference.setAlpha(0.38f);
  672. }
  673. }
  674. }
  675. private class ScreenSecurityChangeListener implements OnPreferenceValueChangedListener<Boolean> {
  676. @Override
  677. public void onChanged(Boolean newValue) {
  678. if (newValue) {
  679. if (getActivity() != null) {
  680. getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
  681. }
  682. } else {
  683. if (getActivity() != null) {
  684. getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
  685. }
  686. }
  687. }
  688. }
  689. private class ProxyCredentialsChangeListener implements OnPreferenceValueChangedListener<Boolean> {
  690. @Override
  691. public void onChanged(Boolean newValue) {
  692. if (newValue) {
  693. showProxyCredentials();
  694. } else {
  695. hideProxyCredentials();
  696. }
  697. }
  698. }
  699. private class ProxyTypeChangeListener implements OnPreferenceValueChangedListener<String> {
  700. @Override
  701. public void onChanged(String newValue) {
  702. if ("No proxy".equals(newValue)) {
  703. hideProxySettings();
  704. } else {
  705. switch (newValue) {
  706. case "HTTP":
  707. if (proxyPortEditText != null) {
  708. proxyPortEditText.setValue("3128");
  709. }
  710. break;
  711. case "DIRECT":
  712. if (proxyPortEditText != null) {
  713. proxyPortEditText.setValue("8080");
  714. }
  715. break;
  716. case "SOCKS":
  717. if (proxyPortEditText != null) {
  718. proxyPortEditText.setValue("1080");
  719. }
  720. break;
  721. default:
  722. break;
  723. }
  724. showProxySettings();
  725. }
  726. }
  727. }
  728. private class ThemeChangeListener implements OnPreferenceValueChangedListener<String> {
  729. @Override
  730. public void onChanged(String newValue) {
  731. NextcloudTalkApplication.Companion.setAppTheme(newValue);
  732. }
  733. }
  734. private class PhoneBookIntegrationChangeListener implements OnPreferenceValueChangedListener<Boolean> {
  735. private final Controller controller;
  736. public PhoneBookIntegrationChangeListener(Controller controller) {
  737. this.controller = controller;
  738. }
  739. @Override
  740. public void onChanged(Boolean isEnabled) {
  741. if (isEnabled) {
  742. if(ContactAddressBookWorker.Companion.checkPermission(controller, context)){
  743. checkForPhoneNumber();
  744. }
  745. } else {
  746. ContactAddressBookWorker.Companion.deleteAll();
  747. }
  748. }
  749. }
  750. private void checkForPhoneNumber() {
  751. ncApi.getUserData(
  752. ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
  753. ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl())
  754. ).subscribeOn(Schedulers.io())
  755. .observeOn(AndroidSchedulers.mainThread())
  756. .subscribe(new Observer<UserProfileOverall>() {
  757. @Override
  758. public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
  759. }
  760. @Override
  761. public void onNext(@io.reactivex.annotations.NonNull UserProfileOverall userProfileOverall) {
  762. if (userProfileOverall.getOcs().getData().getPhone().isEmpty()) {
  763. askForPhoneNumber();
  764. } else {
  765. Log.d(TAG, "phone number already set");
  766. }
  767. }
  768. @Override
  769. public void onError(@io.reactivex.annotations.NonNull Throwable e) {
  770. }
  771. @Override
  772. public void onComplete() {
  773. }
  774. });
  775. }
  776. private void askForPhoneNumber() {
  777. final LinearLayout phoneNumberLayoutWrapper = new LinearLayout(getActivity());
  778. phoneNumberLayoutWrapper.setOrientation(LinearLayout.VERTICAL);
  779. phoneNumberLayoutWrapper.setPadding(50, 0, 50, 0);
  780. final TextInputLayout phoneNumberInputLayout = new TextInputLayout(getActivity());
  781. final EditText phoneNumberField = new EditText(getActivity());
  782. phoneNumberInputLayout.setHelperTextColor(ColorStateList.valueOf(getResources().getColor(R.color.nc_darkRed)));
  783. phoneNumberField.setInputType(InputType.TYPE_CLASS_PHONE);
  784. phoneNumberField.setText("+");
  785. phoneNumberField.addTextChangedListener(new TextWatcher() {
  786. public void afterTextChanged(Editable s) {
  787. }
  788. public void beforeTextChanged(CharSequence s, int start, int count, int after) {
  789. }
  790. public void onTextChanged(CharSequence s, int start, int before, int count) {
  791. phoneNumberInputLayout.setHelperText("");
  792. }
  793. });
  794. phoneNumberInputLayout.addView(phoneNumberField);
  795. phoneNumberLayoutWrapper.addView(phoneNumberInputLayout);
  796. AlertDialog dialog = new AlertDialog.Builder(getActivity())
  797. .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title)
  798. .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description)
  799. .setView(phoneNumberLayoutWrapper)
  800. .setPositiveButton(context.getResources().getString(R.string.nc_common_set), null)
  801. .setNegativeButton(context.getResources().getString(R.string.nc_common_skip), null)
  802. .create();
  803. dialog.setOnShowListener(new DialogInterface.OnShowListener() {
  804. @Override
  805. public void onShow(DialogInterface dialogInterface) {
  806. Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
  807. button.setOnClickListener(new View.OnClickListener() {
  808. @Override
  809. public void onClick(View view) {
  810. setPhoneNumber(phoneNumberInputLayout, dialog);
  811. }
  812. });
  813. }
  814. });
  815. dialog.show();
  816. }
  817. private void setPhoneNumber(TextInputLayout textInputLayout, AlertDialog dialog) {
  818. String phoneNumber = textInputLayout.getEditText().getText().toString();
  819. ncApi.setUserData(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
  820. ApiUtils.getUrlForUserData(currentUser.getBaseUrl(), currentUser.getUserId()), "phone", phoneNumber
  821. ).subscribeOn(Schedulers.io())
  822. .observeOn(AndroidSchedulers.mainThread())
  823. .subscribe(new Observer<GenericOverall>() {
  824. @Override
  825. public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
  826. }
  827. @Override
  828. public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) {
  829. int statusCode = genericOverall.ocs.meta.statusCode;
  830. if (statusCode == 200) {
  831. dialog.dismiss();
  832. Toast.makeText(context, context.getResources().getString(
  833. R.string.nc_settings_phone_book_integration_phone_number_dialog_success),
  834. Toast.LENGTH_LONG).show();
  835. } else {
  836. textInputLayout.setHelperText(context.getResources().getString(
  837. R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid));
  838. Log.d(TAG, "failed to set phoneNumber. statusCode=" + statusCode);
  839. }
  840. }
  841. @Override
  842. public void onError(@io.reactivex.annotations.NonNull Throwable e) {
  843. textInputLayout.setHelperText(context.getResources().getString(
  844. R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid));
  845. Log.e(TAG, "setPhoneNumber error", e);
  846. }
  847. @Override
  848. public void onComplete() {
  849. }
  850. });
  851. }
  852. private class ReadPrivacyChangeListener implements OnPreferenceValueChangedListener<Boolean> {
  853. @Override
  854. public void onChanged(Boolean newValue) {
  855. String booleanValue = newValue ? "0" : "1";
  856. String json = "{\"key\": \"read_status_privacy\", \"value\" : " + booleanValue + "}";
  857. ncApi.setReadStatusPrivacy(
  858. ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
  859. ApiUtils.getUrlForUserSettings(currentUser.getBaseUrl()),
  860. RequestBody.create(MediaType.parse("application/json"), json))
  861. .subscribeOn(Schedulers.io())
  862. .observeOn(AndroidSchedulers.mainThread())
  863. .subscribe(new Observer<GenericOverall>() {
  864. @Override
  865. public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
  866. }
  867. @Override
  868. public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) {
  869. }
  870. @Override
  871. public void onError(@io.reactivex.annotations.NonNull Throwable e) {
  872. appPreferences.setReadPrivacy(!newValue);
  873. ((Checkable) readPrivacyPreference.findViewById(R.id.mp_checkable)).setChecked(!newValue);
  874. }
  875. @Override
  876. public void onComplete() {
  877. }
  878. });
  879. }
  880. }
  881. }