SettingsActivity.kt 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. /*
  2. * Nextcloud Talk application
  3. *
  4. * @author Andy Scherzinger
  5. * @author Mario Danic
  6. * @author Tim Krüger
  7. * @author Ezhil Shanmugham
  8. * Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
  9. * Copyright (C) 2021-2022 Andy Scherzinger <info@andy-scherzinger.de>
  10. * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
  11. * Copyright (C) 2023 Ezhil Shanmugham <ezhil56x.contact@gmail.com>
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation, either version 3 of the License, or
  16. * at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26. package com.nextcloud.talk.settings
  27. import android.animation.Animator
  28. import android.animation.AnimatorListenerAdapter
  29. import android.app.Activity
  30. import android.app.KeyguardManager
  31. import android.content.Context
  32. import android.content.DialogInterface
  33. import android.content.DialogInterface.OnShowListener
  34. import android.content.Intent
  35. import android.content.pm.PackageManager
  36. import android.content.res.ColorStateList
  37. import android.graphics.PorterDuff
  38. import android.graphics.drawable.ColorDrawable
  39. import android.media.RingtoneManager
  40. import android.net.Uri
  41. import android.os.Build
  42. import android.os.Bundle
  43. import android.provider.Settings
  44. import android.security.KeyChain
  45. import android.text.Editable
  46. import android.text.InputType
  47. import android.text.TextUtils
  48. import android.text.TextWatcher
  49. import android.util.Log
  50. import android.view.View
  51. import android.view.WindowManager
  52. import android.widget.EditText
  53. import android.widget.LinearLayout
  54. import androidx.appcompat.app.AlertDialog
  55. import androidx.appcompat.view.ContextThemeWrapper
  56. import androidx.core.content.ContextCompat
  57. import androidx.core.view.ViewCompat
  58. import androidx.work.OneTimeWorkRequest
  59. import androidx.work.WorkInfo
  60. import androidx.work.WorkManager
  61. import autodagger.AutoInjector
  62. import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
  63. import com.google.android.material.dialog.MaterialAlertDialogBuilder
  64. import com.google.android.material.snackbar.Snackbar
  65. import com.google.android.material.textfield.TextInputLayout
  66. import com.nextcloud.android.common.ui.theme.utils.ColorRole
  67. import com.nextcloud.talk.BuildConfig
  68. import com.nextcloud.talk.R
  69. import com.nextcloud.talk.activities.BaseActivity
  70. import com.nextcloud.talk.api.NcApi
  71. import com.nextcloud.talk.application.NextcloudTalkApplication
  72. import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.setAppTheme
  73. import com.nextcloud.talk.data.user.model.User
  74. import com.nextcloud.talk.databinding.ActivitySettingsBinding
  75. import com.nextcloud.talk.jobs.AccountRemovalWorker
  76. import com.nextcloud.talk.jobs.CapabilitiesWorker
  77. import com.nextcloud.talk.jobs.ContactAddressBookWorker
  78. import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.checkPermission
  79. import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.deleteAll
  80. import com.nextcloud.talk.models.json.generic.GenericOverall
  81. import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
  82. import com.nextcloud.talk.profile.ProfileActivity
  83. import com.nextcloud.talk.users.UserManager
  84. import com.nextcloud.talk.utils.ApiUtils
  85. import com.nextcloud.talk.utils.DisplayUtils
  86. import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment
  87. import com.nextcloud.talk.utils.NotificationUtils
  88. import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
  89. import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri
  90. import com.nextcloud.talk.utils.SecurityUtils
  91. import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
  92. import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
  93. import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
  94. import io.reactivex.Observer
  95. import io.reactivex.android.schedulers.AndroidSchedulers
  96. import io.reactivex.disposables.Disposable
  97. import io.reactivex.schedulers.Schedulers
  98. import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener
  99. import okhttp3.MediaType.Companion.toMediaTypeOrNull
  100. import okhttp3.RequestBody.Companion.toRequestBody
  101. import java.net.URI
  102. import java.net.URISyntaxException
  103. import java.util.Locale
  104. import javax.inject.Inject
  105. @AutoInjector(NextcloudTalkApplication::class)
  106. class SettingsActivity : BaseActivity() {
  107. private lateinit var binding: ActivitySettingsBinding
  108. @Inject
  109. lateinit var ncApi: NcApi
  110. @Inject
  111. lateinit var userManager: UserManager
  112. @Inject
  113. lateinit var currentUserProvider: CurrentUserProviderNew
  114. private var currentUser: User? = null
  115. private var credentials: String? = null
  116. private var proxyTypeChangeListener: OnPreferenceValueChangedListener<String>? = null
  117. private var proxyCredentialsChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
  118. private var screenSecurityChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
  119. private var screenLockChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
  120. private var screenLockTimeoutChangeListener: OnPreferenceValueChangedListener<String?>? = null
  121. private var themeChangeListener: OnPreferenceValueChangedListener<String?>? = null
  122. private var readPrivacyChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
  123. private var typingStatusChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
  124. private var phoneBookIntegrationChangeListener: OnPreferenceValueChangedListener<Boolean>? = null
  125. private var profileQueryDisposable: Disposable? = null
  126. private var dbQueryDisposable: Disposable? = null
  127. override fun onCreate(savedInstanceState: Bundle?) {
  128. super.onCreate(savedInstanceState)
  129. NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
  130. binding = ActivitySettingsBinding.inflate(layoutInflater)
  131. setupActionBar()
  132. setContentView(binding.root)
  133. setupSystemColors()
  134. binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
  135. getCurrentUser()
  136. // setupSettingsScreen()
  137. setupLicenceSetting()
  138. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
  139. binding.settingsIncognitoKeyboard.visibility = View.GONE
  140. }
  141. binding.settingsScreenLockSummary.text = String.format(
  142. Locale.getDefault(),
  143. resources!!.getString(R.string.nc_settings_screen_lock_desc),
  144. resources!!.getString(R.string.nc_app_product_name)
  145. )
  146. setupPrivacyUrl()
  147. setupSourceCodeUrl()
  148. binding.settingsVersionSummary.text = String.format("v" + BuildConfig.VERSION_NAME)
  149. setupSoundSettings()
  150. setupPhoneBookIntegration()
  151. setupClientCertView()
  152. }
  153. override fun onResume() {
  154. super.onResume()
  155. supportActionBar?.show()
  156. dispose(null)
  157. loadCapabilitiesAndUpdateSettings()
  158. binding.settingsVersion.setOnClickListener {
  159. sendLogs()
  160. }
  161. if (!TextUtils.isEmpty(currentUser!!.clientCertificate)) {
  162. binding.settingsClientCertTitle.setText(R.string.nc_client_cert_change)
  163. } else {
  164. binding.settingsClientCertTitle.setText(R.string.nc_client_cert_setup)
  165. }
  166. setupCheckables()
  167. setupScreenLockSetting()
  168. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  169. binding.settingsNotificationsTitle.text = resources!!.getString(
  170. R.string.nc_settings_notification_sounds_post_oreo
  171. )
  172. }
  173. val callRingtoneUri = getCallRingtoneUri(context, (appPreferences))
  174. binding.callsRingtone.text = getRingtoneName(context, callRingtoneUri)
  175. val messageRingtoneUri = getMessageRingtoneUri(context, (appPreferences))
  176. binding.messagesRingtone.text = getRingtoneName(context, messageRingtoneUri)
  177. setupProxyTypeSettings()
  178. setupProxyCredentialSettings()
  179. registerChangeListeners()
  180. if (currentUser != null) {
  181. binding.domainText.text = Uri.parse(currentUser!!.baseUrl).host
  182. setupServerAgeWarning()
  183. if (currentUser!!.displayName != null) {
  184. binding.nameText.text = currentUser!!.displayName
  185. }
  186. DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false)
  187. setupProfileQueryDisposable()
  188. binding.settingsRemoveAccount.setOnClickListener {
  189. showRemoveAccountWarning()
  190. }
  191. }
  192. setupMessageView()
  193. binding.settingsName.visibility = View.VISIBLE
  194. binding.settingsName.setOnClickListener {
  195. val intent = Intent(this, ProfileActivity::class.java)
  196. startActivity(intent)
  197. }
  198. themeTitles()
  199. themeSwitchPreferences()
  200. }
  201. private fun loadCapabilitiesAndUpdateSettings() {
  202. val capabilitiesWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java).build()
  203. WorkManager.getInstance(context).enqueue(capabilitiesWork)
  204. WorkManager.getInstance(context).getWorkInfoByIdLiveData(capabilitiesWork.id)
  205. .observe(this) { workInfo ->
  206. if (workInfo?.state == WorkInfo.State.SUCCEEDED) {
  207. getCurrentUser()
  208. setupCheckables()
  209. }
  210. }
  211. }
  212. private fun setupActionBar() {
  213. setSupportActionBar(binding.settingsToolbar)
  214. binding.settingsToolbar.setNavigationOnClickListener {
  215. onBackPressedDispatcher.onBackPressed()
  216. }
  217. supportActionBar?.setDisplayHomeAsUpEnabled(true)
  218. supportActionBar?.setDisplayShowHomeEnabled(true)
  219. supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent, null)))
  220. supportActionBar?.title = context.getString(R.string.nc_settings)
  221. viewThemeUtils.material.themeToolbar(binding.settingsToolbar)
  222. }
  223. private fun getCurrentUser() {
  224. currentUser = currentUserProvider.currentUser.blockingGet()
  225. credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
  226. }
  227. private fun setupPhoneBookIntegration() {
  228. if (CapabilitiesUtilNew.isPhoneBookIntegrationAvailable(currentUser!!)) {
  229. binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
  230. } else {
  231. binding.settingsPhoneBookIntegration.visibility = View.GONE
  232. }
  233. }
  234. private fun setupSoundSettings() {
  235. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  236. binding.settingsCallSound.setOnClickListener {
  237. val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
  238. intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
  239. intent.putExtra(
  240. Settings.EXTRA_CHANNEL_ID,
  241. NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name
  242. )
  243. startActivity(intent)
  244. }
  245. binding.settingsMessageSound.setOnClickListener {
  246. val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
  247. intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
  248. intent.putExtra(
  249. Settings.EXTRA_CHANNEL_ID,
  250. NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_MESSAGES_V4.name
  251. )
  252. startActivity(intent)
  253. }
  254. } else {
  255. Log.e(TAG, "setupSoundSettings currently not supported for versions < Build.VERSION_CODES.O")
  256. }
  257. }
  258. private fun setupSourceCodeUrl() {
  259. if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_source_code_url))) {
  260. binding.settingsSourceCode.setOnClickListener {
  261. startActivity(
  262. Intent(
  263. Intent.ACTION_VIEW,
  264. Uri.parse(resources!!.getString(R.string.nc_source_code_url))
  265. )
  266. )
  267. }
  268. } else {
  269. binding.settingsSourceCode.visibility = View.GONE
  270. }
  271. }
  272. private fun setupPrivacyUrl() {
  273. if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_privacy_url))) {
  274. binding.settingsPrivacy.setOnClickListener {
  275. startActivity(
  276. Intent(
  277. Intent.ACTION_VIEW,
  278. Uri.parse(resources!!.getString(R.string.nc_privacy_url))
  279. )
  280. )
  281. }
  282. } else {
  283. binding.settingsPrivacy.visibility = View.GONE
  284. }
  285. }
  286. private fun setupLicenceSetting() {
  287. if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_gpl3_url))) {
  288. binding.settingsLicence.setOnClickListener {
  289. startActivity(
  290. Intent(
  291. Intent.ACTION_VIEW,
  292. Uri.parse(resources!!.getString(R.string.nc_gpl3_url))
  293. )
  294. )
  295. }
  296. } else {
  297. binding.settingsLicence.visibility = View.GONE
  298. }
  299. }
  300. private fun setupClientCertView() {
  301. var host: String? = null
  302. var port = -1
  303. val uri: URI
  304. try {
  305. uri = URI(currentUser!!.baseUrl)
  306. host = uri.host
  307. port = uri.port
  308. Log.d(TAG, "uri is $uri")
  309. } catch (e: URISyntaxException) {
  310. Log.e(TAG, "Failed to create uri")
  311. }
  312. binding.settingsClientCert.setOnClickListener {
  313. KeyChain.choosePrivateKeyAlias(
  314. this,
  315. { alias: String? ->
  316. var finalAlias: String? = alias
  317. runOnUiThread {
  318. if (finalAlias != null) {
  319. binding.settingsClientCertTitle.setText(R.string.nc_client_cert_change)
  320. } else {
  321. binding.settingsClientCertTitle.setText(R.string.nc_client_cert_setup)
  322. }
  323. }
  324. if (finalAlias == null) {
  325. finalAlias = ""
  326. }
  327. Log.d(TAG, "host: $host and port: $port")
  328. currentUser!!.clientCertificate = finalAlias
  329. userManager.updateOrCreateUser(currentUser!!)
  330. },
  331. arrayOf("RSA", "EC"),
  332. null,
  333. host,
  334. port,
  335. currentUser!!.clientCertificate
  336. )
  337. }
  338. }
  339. private fun registerChangeListeners() {
  340. appPreferences.registerProxyTypeListener(ProxyTypeChangeListener().also { proxyTypeChangeListener = it })
  341. appPreferences.registerProxyCredentialsListener(
  342. ProxyCredentialsChangeListener().also {
  343. proxyCredentialsChangeListener = it
  344. }
  345. )
  346. appPreferences.registerScreenSecurityListener(
  347. ScreenSecurityChangeListener().also {
  348. screenSecurityChangeListener = it
  349. }
  350. )
  351. var pos = getStringArray(R.array.screen_lock_timeout_entry_values).indexOf(appPreferences.screenLockTimeout)
  352. binding.settingsScreenLockTimeoutLayoutDropdown.setText(
  353. getStringArray(R.array.screen_lock_timeout_descriptions)[pos]
  354. )
  355. binding.settingsScreenLockTimeoutLayoutDropdown.setSimpleItems(R.array.screen_lock_timeout_descriptions)
  356. binding.settingsScreenLockTimeoutLayoutDropdown.setOnItemClickListener { _, _, position, _ ->
  357. val entryVal: String = resources.getStringArray(R.array.screen_lock_timeout_entry_values)[position]
  358. appPreferences.screenLockTimeout = entryVal
  359. }
  360. appPreferences.registerScreenLockListener(ScreenLockListener().also { screenLockChangeListener = it })
  361. appPreferences.registerScreenLockTimeoutListener(
  362. ScreenLockTimeoutListener().also {
  363. screenLockTimeoutChangeListener = it
  364. }
  365. )
  366. pos = getStringArray(R.array.theme_entry_values).indexOf(appPreferences.theme)
  367. binding.settingsTheme.setText(getStringArray(R.array.theme_descriptions)[pos])
  368. binding.settingsTheme.setSimpleItems(R.array.theme_descriptions)
  369. binding.settingsTheme.setOnItemClickListener { _, _, position, _ ->
  370. val entryVal: String = getStringArray(R.array.theme_entry_values)[position]
  371. appPreferences.theme = entryVal
  372. }
  373. appPreferences.registerThemeChangeListener(ThemeChangeListener().also { themeChangeListener = it })
  374. appPreferences.registerPhoneBookIntegrationChangeListener(
  375. PhoneBookIntegrationChangeListener(this).also {
  376. phoneBookIntegrationChangeListener = it
  377. }
  378. )
  379. appPreferences.registerReadPrivacyChangeListener(
  380. ReadPrivacyChangeListener().also {
  381. readPrivacyChangeListener = it
  382. }
  383. )
  384. binding.settingsPrivacy.setOnClickListener {
  385. readPrivacyChangeListener!!.onChanged(!binding.settingsReadPrivacySwitch.isChecked)
  386. }
  387. appPreferences.registerTypingStatusChangeListener(
  388. TypingStatusChangeListener().also {
  389. typingStatusChangeListener = it
  390. }
  391. )
  392. }
  393. fun sendLogs() {
  394. if (resources!!.getBoolean(R.bool.nc_is_debug)) {
  395. sendMailWithAttachment((context))
  396. }
  397. }
  398. private fun showRemoveAccountWarning() {
  399. binding.messageText.context?.let {
  400. val materialAlertDialogBuilder = MaterialAlertDialogBuilder(it)
  401. .setTitle(R.string.nc_settings_remove_account)
  402. .setMessage(R.string.nc_settings_remove_confirmation)
  403. .setPositiveButton(R.string.nc_settings_remove) { _, _ ->
  404. removeCurrentAccount()
  405. }
  406. .setNegativeButton(R.string.nc_cancel) { _, _ ->
  407. // unused atm
  408. }
  409. viewThemeUtils.dialog.colorMaterialAlertDialogBackground(
  410. it,
  411. materialAlertDialogBuilder
  412. )
  413. val dialog = materialAlertDialogBuilder.show()
  414. viewThemeUtils.platform.colorTextButtons(
  415. dialog.getButton(AlertDialog.BUTTON_POSITIVE),
  416. dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
  417. )
  418. }
  419. }
  420. private fun removeCurrentAccount() {
  421. val otherUserExists = userManager.scheduleUserForDeletionWithId(currentUser!!.id!!).blockingGet()
  422. val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
  423. WorkManager.getInstance(this).enqueue(accountRemovalWork)
  424. if (otherUserExists) {
  425. // TODO: find better solution once Conductor is removed
  426. finish()
  427. startActivity(intent)
  428. } else if (!otherUserExists) {
  429. Log.d(TAG, "No other users found. AccountRemovalWorker will restart the app.")
  430. }
  431. }
  432. private fun getRingtoneName(context: Context, ringtoneUri: Uri?): String {
  433. return if (ringtoneUri == null) {
  434. resources!!.getString(R.string.nc_settings_no_ringtone)
  435. } else if ((NotificationUtils.DEFAULT_CALL_RINGTONE_URI == ringtoneUri.toString()) ||
  436. (NotificationUtils.DEFAULT_MESSAGE_RINGTONE_URI == ringtoneUri.toString())
  437. ) {
  438. resources!!.getString(R.string.nc_settings_default_ringtone)
  439. } else {
  440. val r = RingtoneManager.getRingtone(context, ringtoneUri)
  441. r.getTitle(context)
  442. }
  443. }
  444. private fun themeSwitchPreferences() {
  445. binding.run {
  446. listOf(
  447. settingsScreenLockSwitch,
  448. settingsScreenSecuritySwitch,
  449. settingsIncognitoKeyboardSwitch,
  450. settingsPhoneBookIntegrationSwitch,
  451. settingsReadPrivacySwitch,
  452. settingsTypingStatusSwitch,
  453. settingsProxyUseCredentialsSwitch
  454. ).forEach(viewThemeUtils.talk::colorSwitch)
  455. }
  456. }
  457. private fun themeTitles() {
  458. binding.run {
  459. listOf(
  460. settingsNotificationsTitle,
  461. settingsAboutTitle,
  462. settingsAdvancedTitle,
  463. settingsAppearanceTitle,
  464. settingsPrivacyTitle
  465. ).forEach(viewThemeUtils.platform::colorTextView)
  466. }
  467. }
  468. private fun setupProxyTypeSettings() {
  469. if (appPreferences.proxyType == null) {
  470. appPreferences.proxyType = resources.getString(R.string.nc_no_proxy)
  471. }
  472. binding.settingsProxyChoice.setText(appPreferences.proxyType)
  473. binding.settingsProxyChoice.setSimpleItems(R.array.proxy_type_descriptions)
  474. binding.settingsProxyChoice.setOnItemClickListener { _, _, position, _ ->
  475. val entryVal = getStringArray(R.array.proxy_type_descriptions)[position]
  476. appPreferences.proxyType = entryVal
  477. }
  478. binding.settingsProxyHostEdit.setText(appPreferences.proxyHost)
  479. binding.settingsProxyHostEdit.setOnFocusChangeListener { _, hasFocus ->
  480. if (!hasFocus) {
  481. appPreferences.proxyHost = binding.settingsProxyHostEdit.text.toString()
  482. }
  483. }
  484. binding.settingsProxyPortEdit.setText(appPreferences.proxyPort)
  485. binding.settingsProxyPortEdit.setOnFocusChangeListener { _, hasFocus ->
  486. if (!hasFocus) {
  487. appPreferences.proxyPort = binding.settingsProxyPortEdit.text.toString()
  488. }
  489. }
  490. binding.settingsProxyUsernameEdit.setText(appPreferences.proxyUsername)
  491. binding.settingsProxyUsernameEdit.setOnFocusChangeListener { _, hasFocus ->
  492. if (!hasFocus) {
  493. appPreferences.proxyUsername = binding.settingsProxyUsernameEdit.text.toString()
  494. }
  495. }
  496. binding.settingsProxyPasswordEdit.setText(appPreferences.proxyPassword)
  497. binding.settingsProxyPasswordEdit.setOnFocusChangeListener { _, hasFocus ->
  498. if (!hasFocus) {
  499. appPreferences.proxyPassword = binding.settingsProxyPasswordEdit.text.toString()
  500. }
  501. }
  502. if (("No proxy" == appPreferences.proxyType) || appPreferences.proxyType == null) {
  503. hideProxySettings()
  504. } else {
  505. showProxySettings()
  506. }
  507. }
  508. private fun setupProxyCredentialSettings() {
  509. if (appPreferences.proxyCredentials) {
  510. showProxyCredentials()
  511. } else {
  512. hideProxyCredentials()
  513. }
  514. }
  515. private fun setupMessageView() {
  516. if (ApplicationWideMessageHolder.getInstance().messageType != null) {
  517. when (ApplicationWideMessageHolder.getInstance().messageType) {
  518. ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED -> {
  519. binding.messageText.let {
  520. viewThemeUtils.platform.colorTextView(it, ColorRole.PRIMARY)
  521. it.text = resources!!.getString(R.string.nc_settings_account_updated)
  522. binding.messageText.visibility = View.VISIBLE
  523. }
  524. }
  525. ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK -> {
  526. binding.messageText.let {
  527. it.setTextColor(resources!!.getColor(R.color.nc_darkRed, null))
  528. it.text = resources!!.getString(R.string.nc_settings_wrong_account)
  529. binding.messageText.visibility = View.VISIBLE
  530. viewThemeUtils.platform.colorTextView(it, ColorRole.PRIMARY)
  531. it.text = resources!!.getString(R.string.nc_Server_account_imported)
  532. binding.messageText.visibility = View.VISIBLE
  533. }
  534. }
  535. ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED -> {
  536. binding.messageText.let {
  537. viewThemeUtils.platform.colorTextView(it, ColorRole.PRIMARY)
  538. it.text = resources!!.getString(R.string.nc_Server_account_imported)
  539. binding.messageText.visibility = View.VISIBLE
  540. }
  541. }
  542. ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT -> {
  543. binding.messageText.let {
  544. it.setTextColor(resources!!.getColor(R.color.nc_darkRed, null))
  545. it.text = resources!!.getString(R.string.nc_server_failed_to_import_account)
  546. binding.messageText.visibility = View.VISIBLE
  547. }
  548. }
  549. else -> binding.messageText.visibility = View.GONE
  550. }
  551. ApplicationWideMessageHolder.getInstance().messageType = null
  552. binding.messageText.animate()
  553. ?.translationY(0f)
  554. ?.alpha(0.0f)
  555. ?.setDuration(DURATION)
  556. ?.setStartDelay(START_DELAY)
  557. ?.setListener(object : AnimatorListenerAdapter() {
  558. override fun onAnimationEnd(animation: Animator) {
  559. super.onAnimationEnd(animation)
  560. binding.messageText.visibility = View.GONE
  561. }
  562. })
  563. } else {
  564. binding.messageText.visibility = View.GONE
  565. }
  566. }
  567. private fun setupProfileQueryDisposable() {
  568. profileQueryDisposable = ncApi.getUserProfile(
  569. credentials,
  570. ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)
  571. )
  572. .subscribeOn(Schedulers.io())
  573. .observeOn(AndroidSchedulers.mainThread())
  574. .subscribe(
  575. { userProfileOverall: UserProfileOverall ->
  576. var displayName: String? = null
  577. if (!TextUtils.isEmpty(
  578. userProfileOverall.ocs!!.data!!.displayName
  579. )
  580. ) {
  581. displayName = userProfileOverall.ocs!!.data!!.displayName
  582. } else if (!TextUtils.isEmpty(
  583. userProfileOverall.ocs!!.data!!.displayNameAlt
  584. )
  585. ) {
  586. displayName = userProfileOverall.ocs!!.data!!.displayNameAlt
  587. }
  588. if ((!TextUtils.isEmpty(displayName) && !(displayName == currentUser!!.displayName))) {
  589. currentUser!!.displayName = displayName
  590. userManager.updateOrCreateUser(currentUser!!)
  591. binding.nameText.text = currentUser!!.displayName
  592. }
  593. },
  594. { dispose(profileQueryDisposable) },
  595. { dispose(profileQueryDisposable) }
  596. )
  597. }
  598. private fun setupServerAgeWarning() {
  599. when {
  600. CapabilitiesUtilNew.isServerEOL(currentUser!!.capabilities) -> {
  601. binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context), R.color.nc_darkRed))
  602. binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol)
  603. binding.serverAgeWarningIcon.setColorFilter(
  604. ContextCompat.getColor((context), R.color.nc_darkRed),
  605. PorterDuff.Mode.SRC_IN
  606. )
  607. }
  608. CapabilitiesUtilNew.isServerAlmostEOL(currentUser!!) -> {
  609. binding.serverAgeWarningText.setTextColor(
  610. ContextCompat.getColor((context), R.color.nc_darkYellow)
  611. )
  612. binding.serverAgeWarningText.setText(R.string.nc_settings_server_almost_eol)
  613. binding.serverAgeWarningIcon.setColorFilter(
  614. ContextCompat.getColor((context), R.color.nc_darkYellow),
  615. PorterDuff.Mode.SRC_IN
  616. )
  617. }
  618. else -> {
  619. binding.serverAgeWarningTextCard.visibility = View.GONE
  620. }
  621. }
  622. }
  623. private fun setupCheckables() {
  624. binding.settingsScreenSecuritySwitch.isChecked = appPreferences.isScreenSecured
  625. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  626. binding.settingsIncognitoKeyboardSwitch.isChecked = appPreferences.isKeyboardIncognito
  627. }
  628. binding.settingsIncognitoKeyboardSwitch.isChecked = appPreferences.isKeyboardIncognito
  629. if (CapabilitiesUtilNew.isReadStatusAvailable(currentUser!!)) {
  630. binding.settingsReadPrivacySwitch.isChecked = !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!)
  631. } else {
  632. binding.settingsReadPrivacy.visibility = View.GONE
  633. }
  634. setupTypingStatusSetting()
  635. binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled
  636. binding.settingsProxyUseCredentialsSwitch.isChecked = appPreferences.proxyCredentials
  637. binding.settingsProxyUseCredentials.setOnClickListener {
  638. val isChecked = binding.settingsProxyUseCredentialsSwitch.isChecked
  639. binding.settingsProxyUseCredentialsSwitch.isChecked = !isChecked
  640. appPreferences.setProxyNeedsCredentials(!isChecked)
  641. }
  642. binding.settingsScreenLockSwitch.isChecked = appPreferences.isScreenLocked
  643. binding.settingsScreenLock.setOnClickListener {
  644. val isChecked = binding.settingsScreenLockSwitch.isChecked
  645. binding.settingsScreenLockSwitch.isChecked = !isChecked
  646. appPreferences.setScreenLock(!isChecked)
  647. }
  648. binding.settingsReadPrivacy.setOnClickListener {
  649. val isChecked = binding.settingsReadPrivacySwitch.isChecked
  650. binding.settingsReadPrivacySwitch.isChecked = !isChecked
  651. appPreferences.setReadPrivacy(!isChecked)
  652. }
  653. binding.settingsIncognitoKeyboard.setOnClickListener {
  654. val isChecked = binding.settingsIncognitoKeyboardSwitch.isChecked
  655. binding.settingsIncognitoKeyboardSwitch.isChecked = !isChecked
  656. appPreferences.setIncognitoKeyboard(!isChecked)
  657. }
  658. binding.settingsPhoneBookIntegration.setOnClickListener {
  659. val isChecked = binding.settingsPhoneBookIntegrationSwitch.isChecked
  660. binding.settingsPhoneBookIntegrationSwitch.isChecked = !isChecked
  661. appPreferences.setPhoneBookIntegration(!isChecked)
  662. }
  663. binding.settingsScreenSecurity.setOnClickListener {
  664. val isChecked = binding.settingsScreenSecuritySwitch.isChecked
  665. binding.settingsScreenSecuritySwitch.isChecked = !isChecked
  666. appPreferences.setScreenSecurity(!isChecked)
  667. }
  668. binding.settingsTypingStatus.setOnClickListener {
  669. val isChecked = binding.settingsTypingStatusSwitch.isChecked
  670. binding.settingsTypingStatusSwitch.isChecked = !isChecked
  671. appPreferences.setTypingStatus(!isChecked)
  672. }
  673. }
  674. private fun setupTypingStatusSetting() {
  675. if (currentUser!!.externalSignalingServer?.externalSignalingServer?.isNotEmpty() == true) {
  676. binding.settingsTypingStatusOnlyWithHpb.visibility = View.GONE
  677. Log.i(TAG, "Typing Status Available: ${CapabilitiesUtilNew.isTypingStatusAvailable(currentUser!!)}")
  678. if (CapabilitiesUtilNew.isTypingStatusAvailable(currentUser!!)) {
  679. binding.settingsTypingStatusSwitch.isChecked = !CapabilitiesUtilNew.isTypingStatusPrivate(currentUser!!)
  680. } else {
  681. binding.settingsTypingStatus.visibility = View.GONE
  682. }
  683. } else {
  684. Log.i(TAG, "Typing Status not Available")
  685. binding.settingsTypingStatusSwitch.isChecked = false
  686. binding.settingsTypingStatusOnlyWithHpb.visibility = View.VISIBLE
  687. binding.settingsTypingStatus.isEnabled = false
  688. binding.settingsTypingStatusOnlyWithHpb.alpha = DISABLED_ALPHA
  689. binding.settingsTypingStatus.alpha = DISABLED_ALPHA
  690. }
  691. }
  692. private fun setupScreenLockSetting() {
  693. val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
  694. if (keyguardManager.isKeyguardSecure) {
  695. binding.settingsScreenLock.isEnabled = true
  696. binding.settingsScreenLockTimeout.isEnabled = true
  697. binding.settingsScreenLockSwitch.isChecked = appPreferences.isScreenLocked
  698. binding.settingsScreenLockTimeoutLayoutDropdown.isEnabled = appPreferences.isScreenLocked
  699. if (appPreferences.isScreenLocked) {
  700. binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
  701. } else {
  702. binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
  703. }
  704. binding.settingsScreenLock.alpha = ENABLED_ALPHA
  705. } else {
  706. binding.settingsScreenLock.isEnabled = false
  707. binding.settingsScreenLockTimeoutLayoutDropdown.isEnabled = false
  708. appPreferences.removeScreenLock()
  709. appPreferences.removeScreenLockTimeout()
  710. binding.settingsScreenLockSwitch.isChecked = false
  711. binding.settingsScreenLock.alpha = DISABLED_ALPHA
  712. binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
  713. }
  714. }
  715. public override fun onDestroy() {
  716. appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener)
  717. appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener)
  718. appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener)
  719. appPreferences.unregisterScreenLockListener(screenLockChangeListener)
  720. appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener)
  721. appPreferences.unregisterThemeChangeListener(themeChangeListener)
  722. appPreferences.unregisterReadPrivacyChangeListener(readPrivacyChangeListener)
  723. appPreferences.unregisterTypingStatusChangeListener(typingStatusChangeListener)
  724. appPreferences.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener)
  725. super.onDestroy()
  726. }
  727. private fun hideProxySettings() {
  728. appPreferences.removeProxyHost()
  729. appPreferences.removeProxyPort()
  730. appPreferences.removeProxyCredentials()
  731. appPreferences.removeProxyUsername()
  732. appPreferences.removeProxyPassword()
  733. binding.settingsProxyHostLayout.visibility = View.GONE
  734. binding.settingsProxyPortLayout.visibility = View.GONE
  735. binding.settingsProxyUseCredentials.visibility =
  736. View.GONE
  737. hideProxyCredentials()
  738. }
  739. private fun showProxySettings() {
  740. binding.settingsProxyHostLayout.visibility =
  741. View.VISIBLE
  742. binding.settingsProxyPortLayout.visibility =
  743. View.VISIBLE
  744. binding.settingsProxyUseCredentials.visibility =
  745. View.VISIBLE
  746. if (binding.settingsProxyUseCredentialsSwitch.isChecked) showProxyCredentials()
  747. }
  748. private fun showProxyCredentials() {
  749. binding.settingsProxyUsernameLayout.visibility =
  750. View.VISIBLE
  751. binding.settingsProxyPasswordLayout.visibility =
  752. View.VISIBLE
  753. }
  754. private fun hideProxyCredentials() {
  755. appPreferences.removeProxyUsername()
  756. appPreferences.removeProxyPassword()
  757. binding.settingsProxyUsernameLayout.visibility = View.GONE
  758. binding.settingsProxyPasswordLayout.visibility = View.GONE
  759. }
  760. private fun dispose(disposable: Disposable?) {
  761. if (disposable != null && !disposable.isDisposed) {
  762. disposable.dispose()
  763. } else if (disposable == null) {
  764. disposeProfileQueryDisposable()
  765. disposeDbQueryDisposable()
  766. }
  767. }
  768. private fun disposeDbQueryDisposable() {
  769. if (dbQueryDisposable != null && !dbQueryDisposable!!.isDisposed) {
  770. dbQueryDisposable!!.dispose()
  771. dbQueryDisposable = null
  772. } else if (dbQueryDisposable != null) {
  773. dbQueryDisposable = null
  774. }
  775. }
  776. private fun disposeProfileQueryDisposable() {
  777. if (profileQueryDisposable != null && !profileQueryDisposable!!.isDisposed) {
  778. profileQueryDisposable!!.dispose()
  779. profileQueryDisposable = null
  780. } else if (profileQueryDisposable != null) {
  781. profileQueryDisposable = null
  782. }
  783. }
  784. override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
  785. super.onRequestPermissionsResult(requestCode, permissions, grantResults)
  786. if (requestCode == ContactAddressBookWorker.REQUEST_PERMISSION &&
  787. grantResults.isNotEmpty() &&
  788. grantResults[0] == PackageManager.PERMISSION_GRANTED
  789. ) {
  790. WorkManager
  791. .getInstance(this)
  792. .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java).build())
  793. checkForPhoneNumber()
  794. } else {
  795. appPreferences.setPhoneBookIntegration(false)
  796. binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled
  797. Snackbar.make(
  798. binding.root,
  799. context.resources.getString(R.string.no_phone_book_integration_due_to_permissions),
  800. Snackbar.LENGTH_LONG
  801. ).show()
  802. }
  803. }
  804. private inner class ScreenLockTimeoutListener : OnPreferenceValueChangedListener<String?> {
  805. override fun onChanged(newValue: String?) {
  806. SecurityUtils.createKey(appPreferences.screenLockTimeout)
  807. }
  808. }
  809. private inner class ScreenLockListener : OnPreferenceValueChangedListener<Boolean> {
  810. override fun onChanged(newValue: Boolean) {
  811. binding.settingsScreenLockTimeout.isEnabled = newValue
  812. if (newValue) {
  813. binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
  814. } else {
  815. binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
  816. }
  817. }
  818. }
  819. private inner class ScreenSecurityChangeListener : OnPreferenceValueChangedListener<Boolean> {
  820. override fun onChanged(newValue: Boolean) {
  821. if (newValue) {
  822. window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
  823. } else {
  824. window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
  825. }
  826. }
  827. }
  828. private inner class ProxyCredentialsChangeListener : OnPreferenceValueChangedListener<Boolean> {
  829. override fun onChanged(newValue: Boolean) {
  830. if (newValue) {
  831. showProxyCredentials()
  832. } else {
  833. hideProxyCredentials()
  834. }
  835. }
  836. }
  837. private inner class ProxyTypeChangeListener : OnPreferenceValueChangedListener<String> {
  838. @Suppress("Detekt.TooGenericExceptionCaught")
  839. override fun onChanged(newValue: String) {
  840. if (("No proxy" == newValue)) {
  841. hideProxySettings()
  842. } else {
  843. when (newValue) {
  844. "HTTP" -> {
  845. binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_http_value))
  846. appPreferences.proxyPort = getString(R.string.nc_settings_http_value)
  847. }
  848. "DIRECT" -> {
  849. binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_direct_value))
  850. appPreferences.proxyPort = getString(R.string.nc_settings_direct_value)
  851. }
  852. "SOCKS" -> {
  853. binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_socks_value))
  854. appPreferences.proxyPort = getString(R.string.nc_settings_socks_value)
  855. }
  856. else -> {
  857. }
  858. }
  859. showProxySettings()
  860. }
  861. }
  862. }
  863. private inner class ThemeChangeListener : OnPreferenceValueChangedListener<String?> {
  864. override fun onChanged(newValue: String?) {
  865. setAppTheme((newValue)!!)
  866. }
  867. }
  868. private inner class PhoneBookIntegrationChangeListener(private val activity: Activity) :
  869. OnPreferenceValueChangedListener<Boolean> {
  870. override fun onChanged(isEnabled: Boolean) {
  871. if (isEnabled) {
  872. if (checkPermission(activity, (context))) {
  873. checkForPhoneNumber()
  874. }
  875. } else {
  876. deleteAll()
  877. }
  878. }
  879. }
  880. private fun checkForPhoneNumber() {
  881. ncApi.getUserData(
  882. ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
  883. ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)
  884. ).subscribeOn(Schedulers.io())
  885. .observeOn(AndroidSchedulers.mainThread())
  886. .subscribe(object : Observer<UserProfileOverall> {
  887. override fun onSubscribe(d: Disposable) {
  888. // unused atm
  889. }
  890. override fun onNext(userProfileOverall: UserProfileOverall) {
  891. if (userProfileOverall.ocs!!.data!!.phone?.isEmpty() == true) {
  892. askForPhoneNumber()
  893. } else {
  894. Log.d(TAG, "phone number already set")
  895. }
  896. }
  897. override fun onError(e: Throwable) {
  898. // unused atm
  899. }
  900. override fun onComplete() {
  901. // unused atm
  902. }
  903. })
  904. }
  905. private fun askForPhoneNumber() {
  906. val phoneNumberLayoutWrapper = LinearLayout(context)
  907. phoneNumberLayoutWrapper.orientation = LinearLayout.VERTICAL
  908. phoneNumberLayoutWrapper.setPadding(PHONE_NUMBER_SIDE_PADDING, 0, PHONE_NUMBER_SIDE_PADDING, 0)
  909. val phoneNumberInputLayout = TextInputLayout(ContextThemeWrapper(this, R.style.TextInputLayoutTheme))
  910. val phoneNumberField = EditText(context)
  911. phoneNumberInputLayout.setHelperTextColor(
  912. ColorStateList.valueOf(resources!!.getColor(R.color.nc_darkRed, null))
  913. )
  914. phoneNumberField.inputType = InputType.TYPE_CLASS_PHONE
  915. phoneNumberField.setText("+")
  916. phoneNumberField.addTextChangedListener(object : TextWatcher {
  917. override fun afterTextChanged(s: Editable) {
  918. // unused atm
  919. }
  920. override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
  921. // unused atm
  922. }
  923. override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
  924. phoneNumberInputLayout.helperText = ""
  925. }
  926. })
  927. phoneNumberInputLayout.addView(phoneNumberField)
  928. phoneNumberLayoutWrapper.addView(phoneNumberInputLayout)
  929. val dialogBuilder = MaterialAlertDialogBuilder(phoneNumberInputLayout.context)
  930. .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title)
  931. .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description)
  932. .setView(phoneNumberLayoutWrapper)
  933. .setPositiveButton(context.resources.getString(R.string.nc_common_set), null)
  934. .setNegativeButton(context.resources.getString(R.string.nc_common_skip), null)
  935. viewThemeUtils.dialog.colorMaterialAlertDialogBackground(phoneNumberInputLayout.context, dialogBuilder)
  936. val dialog = dialogBuilder.create()
  937. dialog.setOnShowListener(object : OnShowListener {
  938. override fun onShow(dialogInterface: DialogInterface) {
  939. val button = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
  940. button.setOnClickListener(object : View.OnClickListener {
  941. override fun onClick(view: View) {
  942. setPhoneNumber(phoneNumberInputLayout, dialog)
  943. }
  944. })
  945. }
  946. })
  947. dialog.show()
  948. viewThemeUtils.platform.colorTextButtons(
  949. dialog.getButton(AlertDialog.BUTTON_POSITIVE),
  950. dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
  951. )
  952. }
  953. private fun setPhoneNumber(textInputLayout: TextInputLayout, dialog: AlertDialog) {
  954. val phoneNumber = textInputLayout.editText!!.text.toString()
  955. ncApi.setUserData(
  956. ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
  957. ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId),
  958. "phone",
  959. phoneNumber
  960. ).subscribeOn(Schedulers.io())
  961. .observeOn(AndroidSchedulers.mainThread())
  962. .subscribe(object : Observer<GenericOverall> {
  963. override fun onSubscribe(d: Disposable) {
  964. // unused atm
  965. }
  966. override fun onNext(genericOverall: GenericOverall) {
  967. val statusCode = genericOverall.ocs?.meta?.statusCode
  968. if (statusCode == HTTP_CODE) {
  969. dialog.dismiss()
  970. Snackbar.make(
  971. binding.root,
  972. context.resources.getString(
  973. R.string.nc_settings_phone_book_integration_phone_number_dialog_success
  974. ),
  975. Snackbar.LENGTH_LONG
  976. ).show()
  977. } else {
  978. textInputLayout.helperText = context.resources.getString(
  979. R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid
  980. )
  981. Log.d(TAG, "failed to set phoneNumber. statusCode=$statusCode")
  982. }
  983. }
  984. override fun onError(e: Throwable) {
  985. textInputLayout.helperText = context.resources.getString(
  986. R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid
  987. )
  988. Log.e(TAG, "setPhoneNumber error", e)
  989. }
  990. override fun onComplete() {
  991. // unused atm
  992. }
  993. })
  994. }
  995. private inner class ReadPrivacyChangeListener : OnPreferenceValueChangedListener<Boolean> {
  996. override fun onChanged(newValue: Boolean) {
  997. val booleanValue = if (newValue) "0" else "1"
  998. val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}"
  999. ncApi.setReadStatusPrivacy(
  1000. ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
  1001. ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl),
  1002. json.toRequestBody("application/json".toMediaTypeOrNull())
  1003. )
  1004. .subscribeOn(Schedulers.io())
  1005. .observeOn(AndroidSchedulers.mainThread())
  1006. .subscribe(object : Observer<GenericOverall> {
  1007. override fun onSubscribe(d: Disposable) {
  1008. // unused atm
  1009. }
  1010. override fun onNext(genericOverall: GenericOverall) {
  1011. // unused atm
  1012. }
  1013. override fun onError(e: Throwable) {
  1014. appPreferences.setReadPrivacy(!newValue)
  1015. binding.settingsReadPrivacySwitch.isChecked = !newValue
  1016. }
  1017. override fun onComplete() {
  1018. // unused atm
  1019. }
  1020. })
  1021. }
  1022. }
  1023. private inner class TypingStatusChangeListener : OnPreferenceValueChangedListener<Boolean> {
  1024. override fun onChanged(newValue: Boolean) {
  1025. val booleanValue = if (newValue) "0" else "1"
  1026. val json = "{\"key\": \"typing_privacy\", \"value\" : $booleanValue}"
  1027. ncApi.setTypingStatusPrivacy(
  1028. ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
  1029. ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl),
  1030. json.toRequestBody("application/json".toMediaTypeOrNull())
  1031. )
  1032. .subscribeOn(Schedulers.io())
  1033. .observeOn(AndroidSchedulers.mainThread())
  1034. .subscribe(object : Observer<GenericOverall> {
  1035. override fun onSubscribe(d: Disposable) {
  1036. // unused atm
  1037. }
  1038. override fun onNext(genericOverall: GenericOverall) {
  1039. loadCapabilitiesAndUpdateSettings()
  1040. Log.i(TAG, "onNext called typing status set")
  1041. }
  1042. override fun onError(e: Throwable) {
  1043. appPreferences.setTypingStatus(!newValue)
  1044. binding.settingsTypingStatusSwitch.isChecked = !newValue
  1045. }
  1046. override fun onComplete() {
  1047. // unused atm
  1048. }
  1049. })
  1050. }
  1051. }
  1052. companion object {
  1053. private const val TAG = "SettingsController"
  1054. private const val DURATION: Long = 2500
  1055. private const val START_DELAY: Long = 5000
  1056. private const val DISABLED_ALPHA: Float = 0.38f
  1057. private const val ENABLED_ALPHA: Float = 1.0f
  1058. private const val HTTP_CODE: Int = 200
  1059. private const val PHONE_NUMBER_SIDE_PADDING: Int = 50
  1060. }
  1061. }