SettingsController.java 44 KB

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