diff --git a/app/build.gradle b/app/build.gradle index a879565d0..89d209daf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -160,6 +160,7 @@ configurations.all { } dependencies { + implementation 'androidx.preference:preference-ktx:1.2.0' detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.0") implementation fileTree(include: ['*'], dir: 'libs') @@ -236,7 +237,6 @@ dependencies { implementation 'net.orange-box.storebox:storebox-lib:1.4.0' implementation 'eu.davidea:flexible-adapter:5.1.0' implementation 'eu.davidea:flexible-adapter-ui:1.0.0' - implementation 'com.yarolegovich:mp:1.1.6' implementation 'me.zhanghai.android.effortlesspermissions:library:1.1.0' implementation 'org.apache.commons:commons-lang3:3.12.0' implementation 'com.github.wooplr:Spotlight:1.3' diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index c5453931e..e418b0992 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -42,7 +42,6 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.widget.Toast import androidx.appcompat.app.AlertDialog -import androidx.appcompat.widget.SwitchCompat import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager @@ -97,7 +96,6 @@ import io.reactivex.schedulers.Schedulers import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.parceler.Parcels -import java.lang.Long import java.util.Calendar import java.util.Collections import java.util.Locale @@ -135,7 +133,7 @@ class ConversationInfoActivity : private val workerData: Data? get() { - if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) { + if (!TextUtils.isEmpty(conversationToken)) { val data = Data.Builder() data.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken) data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!) @@ -180,7 +178,7 @@ class ConversationInfoActivity : fetchRoomInfo() - themeCategories() + themeTextViews() themeSwitchPreferences() binding.addParticipantsAction.visibility = GONE @@ -253,39 +251,15 @@ class ConversationInfoActivity : } } - private fun themeCategories() { - binding.run { - listOf( - conversationInfoName, - conversationDescription, - addToFavoritesButton, - participantsListCategory, - addParticipantsAction, - dangerZoneOptions, - categorySharedItems, - conversationSettings, - leaveConversationAction, - deleteConversationAction, - clearConversationHistory, - sharedItemsButton, - binding.guestAccessView.guestAccessSettings, - binding.guestAccessView.guestAccessSettingsAllowGuest, - binding.guestAccessView.guestAccessSettingsPasswordProtection, - binding.guestAccessView.shareConversationButton, - binding.guestAccessView.resendInvitationsButton, - binding.webinarInfoView.webinarSettings, - binding.webinarInfoView.webinarSettingsLobby, - binding.webinarInfoView.startTimeButton, - binding.notificationSettingsView.notificationSettings, - binding.notificationSettingsView.notificationSettingsImportantConversation, - binding.notificationSettingsView.notificationSettingsCallNotifications - ).forEach(viewThemeUtils.talk::ConversationInfoCardView) - } + private fun themeTextViews() { binding.run { listOf( binding.notificationSettingsView.notificationSettingsCategory, binding.webinarInfoView.webinarSettingsCategory, - binding.guestAccessView.guestAccessSettingsCategory + binding.guestAccessView.guestAccessSettingsCategory, + binding.categorySharedItems, + binding.conversationSettings, + binding.participantsListCategory ) }.forEach(viewThemeUtils.platform::colorTextView) } @@ -304,18 +278,17 @@ class ConversationInfoActivity : private fun setupWebinaryView() { if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && webinaryRoomType(conversation!!) && - conversation!!.canModerate(conversationUser!!) + conversation!!.canModerate(conversationUser) ) { - binding?.webinarInfoView?.webinarSettings?.visibility = VISIBLE + binding.webinarInfoView.webinarSettings.visibility = VISIBLE val isLobbyOpenToModeratorsOnly = conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY - (binding?.webinarInfoView?.lobbySwitch as SwitchCompat) - .isChecked = isLobbyOpenToModeratorsOnly + binding.webinarInfoView.lobbySwitch.isChecked = isLobbyOpenToModeratorsOnly reconfigureLobbyTimerView() - binding?.webinarInfoView?.startTimeButton?.setOnClickListener { + binding.webinarInfoView.startTimeButton.setOnClickListener { MaterialDialog(this, BottomSheet(WRAP_CONTENT)).show { val currentTimeCalendar = Calendar.getInstance() if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != 0L) { @@ -335,13 +308,13 @@ class ConversationInfoActivity : } } - (binding?.webinarInfoView?.lobbySwitch as SwitchCompat) - .setOnCheckedChangeListener { _, _ -> - reconfigureLobbyTimerView() - submitLobbyChanges() - } + binding.webinarInfoView.webinarSettingsLobby.setOnClickListener { + binding.webinarInfoView.lobbySwitch.isChecked = !binding.webinarInfoView.lobbySwitch.isChecked + reconfigureLobbyTimerView() + submitLobbyChanges() + } } else { - binding?.webinarInfoView?.webinarSettings?.visibility = GONE + binding.webinarInfoView.webinarSettings.visibility = GONE } } @@ -351,9 +324,7 @@ class ConversationInfoActivity : } private fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { - val isChecked = - (binding?.webinarInfoView?.lobbySwitch as SwitchCompat) - .isChecked + val isChecked = binding.webinarInfoView.lobbySwitch.isChecked if (dateTime != null && isChecked) { conversation!!.lobbyTimer = ( @@ -374,27 +345,24 @@ class ConversationInfoActivity : conversation!!.lobbyTimer != Long.MIN_VALUE && conversation!!.lobbyTimer != 0L ) { - binding?.webinarInfoView?.startTimeButtonSummary?.text = ( + binding.webinarInfoView.startTimeButtonSummary.text = ( dateUtils.getLocalDateTimeStringFromTimestamp( conversation!!.lobbyTimer!! * DateConstants.SECOND_DIVIDER ) ) } else { - binding?.webinarInfoView?.startTimeButtonSummary?.setText(R.string.nc_manual) + binding.webinarInfoView.startTimeButtonSummary.setText(R.string.nc_manual) } if (isChecked) { - binding?.webinarInfoView?.startTimeButton?.visibility = VISIBLE + binding.webinarInfoView.startTimeButton.visibility = VISIBLE } else { - binding?.webinarInfoView?.startTimeButton?.visibility = GONE + binding.webinarInfoView.startTimeButton.visibility = GONE } } - fun submitLobbyChanges() { - val state = if ( - (binding?.webinarInfoView?.lobbySwitch as SwitchCompat) - .isChecked - ) { + private fun submitLobbyChanges() { + val state = if (binding.webinarInfoView.lobbySwitch.isChecked) { 1 } else { 0 @@ -488,11 +456,11 @@ class ConversationInfoActivity : if (participant.sessionId != null) { userItem.isOnline = !participant.sessionId.equals("0") } else { - userItem.isOnline = !participant.sessionIds.isEmpty() + userItem.isOnline = participant.sessionIds.isNotEmpty() } if (participant.calculatedActorType == USERS && - participant.calculatedActorId == conversationUser!!.userId + participant.calculatedActorId == conversationUser.userId ) { ownUserItem = userItem ownUserItem.model.sessionId = "-1" @@ -510,16 +478,13 @@ class ConversationInfoActivity : setupAdapter() - binding.participantsListCategory?.visibility = VISIBLE + binding.participants.visibility = VISIBLE adapter!!.updateDataSet(userItems) } private fun getListOfParticipants() { - var apiVersion = 1 // FIXME Fix API checking with guests? - if (conversationUser != null) { - apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) - } + val apiVersion: Int = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) val fieldMap = HashMap() fieldMap["includeStatus"] = true @@ -528,7 +493,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForParticipants( apiVersion, - conversationUser!!.baseUrl, + conversationUser.baseUrl, conversationToken ), fieldMap @@ -555,7 +520,7 @@ class ConversationInfoActivity : }) } - internal fun addParticipants() { + private fun addParticipants() { val bundle = Bundle() val existingParticipantsId = arrayListOf() @@ -661,13 +626,11 @@ class ConversationInfoActivity : } private fun fetchRoomInfo() { - var apiVersion = 1 + val apiVersion: Int // FIXME Fix API checking with guests? - if (conversationUser != null) { - apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) - } + apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) - ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser!!.baseUrl, conversationToken)) + ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser.baseUrl, conversationToken)) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(object : Observer { @@ -682,64 +645,68 @@ class ConversationInfoActivity : val conversationCopy = conversation if (conversationCopy!!.canModerate(conversationUser)) { - binding?.addParticipantsAction?.visibility = VISIBLE - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "clear-history")) { - binding?.clearConversationHistory?.visibility = VISIBLE + binding.addParticipantsAction.visibility = VISIBLE + if (CapabilitiesUtilNew.hasSpreedFeatureCapability( + conversationUser, + "clear-history" + ) + ) { + binding.clearConversationHistory.visibility = VISIBLE } else { - binding?.clearConversationHistory?.visibility = GONE + binding.clearConversationHistory.visibility = GONE } showOptionsMenu() } else { - binding?.addParticipantsAction?.visibility = GONE - binding?.clearConversationHistory?.visibility = GONE + binding.addParticipantsAction.visibility = GONE + binding.clearConversationHistory.visibility = GONE } if (!isDestroyed) { - binding?.dangerZoneOptions?.visibility = VISIBLE + binding.dangerZoneOptions.visibility = VISIBLE setupWebinaryView() if (!conversation!!.canLeave()) { - binding?.leaveConversationAction?.visibility = GONE + binding.leaveConversationAction.visibility = GONE } else { - binding?.leaveConversationAction?.visibility = VISIBLE + binding.leaveConversationAction.visibility = VISIBLE } if (!conversation!!.canDelete(conversationUser)) { - binding?.deleteConversationAction?.visibility = GONE + binding.deleteConversationAction.visibility = GONE } else { - binding?.deleteConversationAction?.visibility = VISIBLE + binding.deleteConversationAction.visibility = VISIBLE } if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { - binding?.notificationSettingsView?.callNotificationsSwitch?.visibility = GONE + binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE } if (conversation!!.notificationCalls === null) { - binding?.notificationSettingsView?.callNotificationsSwitch?.visibility = GONE + binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE } else { - binding?.notificationSettingsView?.callNotificationsSwitch?.isChecked = + binding.notificationSettingsView.callNotificationsSwitch.isChecked = (conversationCopy.notificationCalls == 1) } getListOfParticipants() - binding?.progressBar?.visibility = GONE + binding.progressBar.visibility = GONE - binding?.conversationInfoName?.visibility = VISIBLE + binding.conversationInfoName.visibility = VISIBLE - binding?.displayNameText?.text = conversation!!.displayName + binding.displayNameText.text = conversation!!.displayName - if (conversation!!.description != null && !conversation!!.description!!.isEmpty()) { - binding?.descriptionText?.text = conversation!!.description - binding?.conversationDescription?.visibility = VISIBLE + if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { + binding.descriptionText.text = conversation!!.description + binding.conversationDescription.visibility = VISIBLE } loadConversationAvatar() adjustNotificationLevelUI() initExpiringMessageOption() - binding?.let { + binding.let { GuestAccessHelper( this@ConversationInfoActivity, it, @@ -748,7 +715,7 @@ class ConversationInfoActivity : ).setupGuestAccess() } - binding?.notificationSettingsView?.notificationSettings?.visibility = VISIBLE + binding.notificationSettingsView.notificationSettings.visibility = VISIBLE } } @@ -774,25 +741,22 @@ class ConversationInfoActivity : binding.conversationSettingsDropdown .setSimpleItems(resources.getStringArray(R.array.message_expiring_descriptions)) binding.conversationSettingsDropdown.setOnItemClickListener { _, _, position, _ -> - val value = resources.getStringArray(R.array.message_expiring_values)[position] - databaseStorageModule!!.saveString("conversation_settings_dropdown", value) + val v: String = resources.getStringArray(R.array.message_expiring_values)[position] + databaseStorageModule!!.saveString("conversation_settings_dropdown", v) } - binding?.conversationSettingsDropdown?.visibility = VISIBLE - binding?.conversationInfoExpireMessagesExplanation?.visibility = VISIBLE + binding.conversationSettingsDropdown.visibility = VISIBLE + binding.conversationInfoExpireMessagesExplanation.visibility = VISIBLE } else { - binding?.conversationSettings?.visibility = GONE + binding.conversationSettings.visibility = GONE } } private fun adjustNotificationLevelUI() { if (conversation != null) { - if ( - conversationUser != null && - CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels") - ) { - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.isEnabled = true - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.alpha = 1.0f + if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.isEnabled = true + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.alpha = 1.0f if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { val stringValue: String = @@ -803,35 +767,36 @@ class ConversationInfoActivity : else -> resources.getString(R.string.nc_notify_me_mention) } - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.setText( + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( stringValue ) } else { setProperNotificationValue(conversation) } } else { - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.isEnabled = false - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.alpha = LOW_EMPHASIS_OPACITY + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.isEnabled = false + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.alpha = + LOW_EMPHASIS_OPACITY setProperNotificationValue(conversation) } - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown - ?.setSimpleItems(resources.getStringArray(R.array.message_notification_levels)) + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown + .setSimpleItems(resources.getStringArray(R.array.message_notification_levels)) } } private fun setProperNotificationValue(conversation: Conversation?) { if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag")) { - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.setText( + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( resources.getString(R.string.nc_notify_me_always) ) } else { - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.setText( + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( resources.getString(R.string.nc_notify_me_mention) ) } } else { - binding?.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.setText( + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( resources.getString(R.string.nc_notify_me_mention) ) } @@ -840,11 +805,23 @@ class ConversationInfoActivity : private fun loadConversationAvatar() { when (conversation!!.type) { Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { - conversation!!.name?.let { binding.avatarImage.loadUserAvatar(conversationUser, it, true, false) } + conversation!!.name?.let { + binding.avatarImage.loadUserAvatar( + conversationUser, + it, + true, + false + ) + } } Conversation.ConversationType.ROOM_GROUP_CALL, Conversation.ConversationType.ROOM_PUBLIC_CALL -> { - binding.avatarImage.loadConversationAvatar(conversationUser, conversation!!, false, viewThemeUtils) + binding.avatarImage.loadConversationAvatar( + conversationUser, + conversation!!, + false, + viewThemeUtils + ) } Conversation.ConversationType.ROOM_SYSTEM -> { @@ -884,7 +861,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForRoomModerators( apiVersion, - conversationUser!!.baseUrl, + conversationUser.baseUrl, conversation!!.token ), participant.attendeeId @@ -899,7 +876,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForRoomModerators( apiVersion, - conversationUser!!.baseUrl, + conversationUser.baseUrl, conversation!!.token ), participant.attendeeId @@ -959,7 +936,7 @@ class ConversationInfoActivity : } } - fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { + private fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { if (apiVersion >= ApiUtils.APIv4) { ncApi.removeAttendeeFromConversation( credentials, @@ -1057,6 +1034,7 @@ class ConversationInfoActivity : } } + @SuppressLint("CheckResult") override fun onItemClick(view: View?, position: Int): Boolean { if (!conversation!!.canModerate(conversationUser)) { return true @@ -1204,26 +1182,28 @@ class ConversationInfoActivity : return true } - fun setUpNotificationSettings(module: DatabaseStorageModule) { - // set up listeners - binding.notificationSettingsView?.importantConversationSwitch?.setOnCheckedChangeListener { _, isChecked -> - module.saveBoolean("important_conversation_switch", isChecked) + private fun setUpNotificationSettings(module: DatabaseStorageModule) { + binding.notificationSettingsView.notificationSettingsImportantConversation.setOnClickListener { + val isChecked = binding.notificationSettingsView.importantConversationSwitch.isChecked + binding.notificationSettingsView.importantConversationSwitch.isChecked = !isChecked + module.saveBoolean("important_conversation_switch", !isChecked) } - - binding.notificationSettingsView?.callNotificationsSwitch?.setOnCheckedChangeListener { _, isChecked -> - module.saveBoolean("call_notifications_switch", isChecked) + binding.notificationSettingsView.notificationSettingsCallNotifications.setOnClickListener { + val isChecked = binding.notificationSettingsView.callNotificationsSwitch.isChecked + binding.notificationSettingsView.callNotificationsSwitch.isChecked = !isChecked + module.saveBoolean("call_notifications_switch", !isChecked) } + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown + .setOnItemClickListener { _, _, position, _ -> + val value = resources.getStringArray(R.array.message_notification_levels_entry_values)[position] + Log.i(TAG, "saved $value to module from $position") + module.saveString("conversation_info_message_notifications_dropdown", value) + } - binding.notificationSettingsView?.conversationInfoMessageNotificationsDropdown?.setOnItemClickListener { - _, _, position, _ -> - val value = resources.getStringArray(R.array.message_notification_levels_entry_values)[position] - module.saveString("conversation_info_message_notifications_dropdown", value) - } - - binding.notificationSettingsView?.importantConversationSwitch?.isChecked = module + binding.notificationSettingsView.importantConversationSwitch.isChecked = module .getBoolean("important_conversation_switch", false) - binding.notificationSettingsView?.callNotificationsSwitch?.isChecked = module + binding.notificationSettingsView.callNotificationsSwitch.isChecked = module .getBoolean("call_notifications_switch", true) } @@ -1232,9 +1212,7 @@ class ConversationInfoActivity : private const val NOTIFICATION_LEVEL_ALWAYS: Int = 1 private const val NOTIFICATION_LEVEL_MENTION: Int = 2 private const val NOTIFICATION_LEVEL_NEVER: Int = 3 - private const val ID_DELETE_CONVERSATION_DIALOG = 0 - private const val ID_CLEAR_CHAT_DIALOG = 1 - private val LOW_EMPHASIS_OPACITY: Float = 0.38f + private const val LOW_EMPHASIS_OPACITY: Float = 0.38f } /** diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt index 018db1be3..5377a8dbd 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt @@ -1,13 +1,11 @@ package com.nextcloud.talk.conversationinfo -import android.annotation.SuppressLint import android.content.Intent import android.util.Log import android.view.LayoutInflater import android.view.View import android.widget.Toast import androidx.appcompat.app.AlertDialog -import androidx.appcompat.widget.SwitchCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.talk.R import com.nextcloud.talk.data.user.model.User @@ -34,15 +32,6 @@ class GuestAccessHelper( private val context = activity.context fun setupGuestAccess() { - val guestAccessAllowSwitch = ( - binding.guestAccessView.allowGuestsSwitch - as SwitchCompat - ) - val guestAccessPasswordSwitch = ( - binding.guestAccessView.passwordProtectionSwitch - as SwitchCompat - ) - if (conversation.canModerate(conversationUser)) { binding.guestAccessView.guestAccessSettings.visibility = View.VISIBLE } else { @@ -50,29 +39,34 @@ class GuestAccessHelper( } if (conversation.type == Conversation.ConversationType.ROOM_PUBLIC_CALL) { - guestAccessAllowSwitch.isChecked = true + binding.guestAccessView.allowGuestsSwitch.isChecked = true showAllOptions() if (conversation.hasPassword) { - guestAccessPasswordSwitch.isChecked = true + binding.guestAccessView.passwordProtectionSwitch.isChecked = true } } else { - guestAccessAllowSwitch.isChecked = false + binding.guestAccessView.allowGuestsSwitch.isChecked = false + hideAllOptions() } - binding.guestAccessView.allowGuestsSwitch.setOnClickListener { + binding.guestAccessView.guestAccessSettingsAllowGuest.setOnClickListener { + val isChecked = binding.guestAccessView.allowGuestsSwitch.isChecked + binding.guestAccessView.allowGuestsSwitch.isChecked = !isChecked conversationsRepository.allowGuests( conversation.token!!, - !guestAccessAllowSwitch.isChecked + !isChecked ).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(AllowGuestsResultObserver()) } - binding.guestAccessView.passwordProtectionSwitch.setOnClickListener { - if (guestAccessPasswordSwitch.isChecked) { + binding.guestAccessView.guestAccessSettingsPasswordProtection.setOnClickListener { + val isChecked = binding.guestAccessView.passwordProtectionSwitch.isChecked + binding.guestAccessView.passwordProtectionSwitch.isChecked = !isChecked + if (isChecked) { conversationsRepository.password("", conversation.token!!).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(PasswordResultObserver(false)) } else { - showPasswordDialog(guestAccessPasswordSwitch) + showPasswordDialog() } } @@ -86,8 +80,7 @@ class GuestAccessHelper( } } - @SuppressLint("InflateParams") - private fun showPasswordDialog(guestAccessPasswordSwitch: SwitchCompat) { + private fun showPasswordDialog() { val builder = MaterialAlertDialogBuilder(activity) builder.apply { val dialogPassword = DialogPasswordBinding.inflate(LayoutInflater.from(context)) @@ -102,7 +95,7 @@ class GuestAccessHelper( .subscribe(PasswordResultObserver(true)) } setNegativeButton(R.string.nc_cancel) { _, _ -> - guestAccessPasswordSwitch.isChecked = false + binding.guestAccessView.passwordProtectionSwitch.isChecked = false } } createDialog(builder) @@ -179,10 +172,7 @@ class GuestAccessHelper( } override fun onComplete() { - ( - binding.guestAccessView.allowGuestsSwitch - as SwitchCompat - ).isChecked = allowGuestsResult.allow + binding.guestAccessView.allowGuestsSwitch.isChecked = allowGuestsResult.allow if (allowGuestsResult.allow) { showAllOptions() } else { @@ -225,12 +215,7 @@ class GuestAccessHelper( } override fun onComplete() { - val guestAccessPasswordSwitch = ( - binding.guestAccessView.passwordProtectionSwitch - as SwitchCompat - ) - guestAccessPasswordSwitch.isChecked = passwordResult.passwordSet && setPassword - + binding.guestAccessView.passwordProtectionSwitch.isChecked = passwordResult.passwordSet && setPassword if (passwordResult.passwordIsWeak) { val builder = MaterialAlertDialogBuilder(activity) builder.apply { diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index dc3e11e73..2cda998f3 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -50,7 +50,6 @@ import android.text.TextWatcher import android.util.Log import android.view.View import android.view.WindowManager -import android.widget.Checkable import android.widget.EditText import android.widget.LinearLayout import android.widget.Toast @@ -61,6 +60,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkInfo import androidx.work.WorkManager import autodagger.AutoInjector +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputLayout import com.nextcloud.android.common.ui.theme.utils.ColorRole @@ -90,7 +90,6 @@ import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.SecurityUtils import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew -import com.nextcloud.talk.utils.preferences.MagicUserInputModule import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers @@ -98,10 +97,9 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody import java.net.URI import java.net.URISyntaxException -import java.util.Arrays import java.util.Locale import javax.inject.Inject @@ -145,26 +143,22 @@ class SettingsActivity : BaseActivity() { getCurrentUser() - registerChangeListeners() - - setupSettingsScreen() + // setupSettingsScreen() setupLicenceSetting() if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { binding.settingsIncognitoKeyboard.visibility = View.GONE } - binding.settingsScreenLock.setSummary( - String.format( - Locale.getDefault(), - resources!!.getString(R.string.nc_settings_screen_lock_desc), - resources!!.getString(R.string.nc_app_product_name) - ) + binding.settingsScreenLockSummary.text = String.format( + Locale.getDefault(), + resources!!.getString(R.string.nc_settings_screen_lock_desc), + resources!!.getString(R.string.nc_app_product_name) ) setupPrivacyUrl() setupSourceCodeUrl() - binding.settingsVersion.setSummary("v" + BuildConfig.VERSION_NAME) + binding.settingsVersionSummary.text = String.format("v" + BuildConfig.VERSION_NAME) setupSoundSettings() @@ -185,51 +179,53 @@ class SettingsActivity : BaseActivity() { } if (!TextUtils.isEmpty(currentUser!!.clientCertificate)) { - binding.settingsClientCert.setTitle(R.string.nc_client_cert_change) + binding.settingsClientCertTitle.setText(R.string.nc_client_cert_change) } else { - binding.settingsClientCert.setTitle(R.string.nc_client_cert_setup) + binding.settingsClientCertTitle.setText(R.string.nc_client_cert_setup) } setupCheckables() setupScreenLockSetting() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - binding.settingsNotificationsCategory.setTitle( - resources!!.getString(R.string.nc_settings_notification_sounds_post_oreo) + binding.settingsNotificationsTitle.text = resources!!.getString( + R.string.nc_settings_notification_sounds_post_oreo ) } val callRingtoneUri = getCallRingtoneUri(context, (appPreferences)) - binding.settingsCallSound.setSummary(getRingtoneName(context, callRingtoneUri)) + binding.callsRingtone.text = getRingtoneName(context, callRingtoneUri) val messageRingtoneUri = getMessageRingtoneUri(context, (appPreferences)) - binding.settingsMessageSound.setSummary(getRingtoneName(context, messageRingtoneUri)) + binding.messagesRingtone.text = getRingtoneName(context, messageRingtoneUri) setupProxyTypeSettings() setupProxyCredentialSettings() + registerChangeListeners() if (currentUser != null) { - binding.baseUrlText.text = Uri.parse(currentUser!!.baseUrl).host + binding.domainText.text = Uri.parse(currentUser!!.baseUrl).host setupServerAgeWarning() if (currentUser!!.displayName != null) { - binding.displayNameText.text = currentUser!!.displayName + binding.nameText.text = currentUser!!.displayName } DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false) setupProfileQueryDisposable() - binding.settingsRemoveAccount.addPreferenceClickListener { + binding.settingsRemoveAccount.setOnClickListener { showRemoveAccountWarning() } } setupMessageView() - binding.avatarContainer.setOnClickListener { + binding.settingsName.visibility = View.VISIBLE + binding.settingsName.setOnClickListener { val intent = Intent(this, ProfileActivity::class.java) startActivity(intent) } - // themeCategories() - // themeSwitchPreferences() + themeTitles() + themeSwitchPreferences() } private fun loadCapabilitiesAndUpdateSettings() { @@ -298,7 +294,7 @@ class SettingsActivity : BaseActivity() { private fun setupSourceCodeUrl() { if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_source_code_url))) { - binding.settingsSourceCode.addPreferenceClickListener { + binding.settingsSourceCode.setOnClickListener { startActivity( Intent( Intent.ACTION_VIEW, @@ -313,7 +309,7 @@ class SettingsActivity : BaseActivity() { private fun setupPrivacyUrl() { if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_privacy_url))) { - binding.settingsPrivacy.addPreferenceClickListener { + binding.settingsPrivacy.setOnClickListener { startActivity( Intent( Intent.ACTION_VIEW, @@ -328,7 +324,7 @@ class SettingsActivity : BaseActivity() { private fun setupLicenceSetting() { if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_gpl3_url))) { - binding.settingsLicence.addPreferenceClickListener { + binding.settingsLicence.setOnClickListener { startActivity( Intent( Intent.ACTION_VIEW, @@ -341,17 +337,6 @@ class SettingsActivity : BaseActivity() { } } - private fun setupSettingsScreen() { - val listWithIntFields: MutableList = ArrayList() - listWithIntFields.add("proxy_port") - binding.settingsScreen.setUserInputModule(MagicUserInputModule(this, listWithIntFields)) - binding.settingsScreen.setVisibilityController( - R.id.settings_proxy_use_credentials, - Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit), - true - ) - } - private fun setupClientCertView() { var host: String? = null var port = -1 @@ -360,11 +345,12 @@ class SettingsActivity : BaseActivity() { uri = URI(currentUser!!.baseUrl) host = uri.host port = uri.port + Log.d(TAG, "uri is $uri") } catch (e: URISyntaxException) { Log.e(TAG, "Failed to create uri") } - binding.settingsClientCert.addPreferenceClickListener { + binding.settingsClientCert.setOnClickListener { KeyChain.choosePrivateKeyAlias( this, { alias: String? -> @@ -372,16 +358,16 @@ class SettingsActivity : BaseActivity() { runOnUiThread { if (finalAlias != null) { - binding.settingsClientCert.setTitle(R.string.nc_client_cert_change) + binding.settingsClientCertTitle.setText(R.string.nc_client_cert_change) } else { - binding.settingsClientCert.setTitle(R.string.nc_client_cert_setup) + binding.settingsClientCertTitle.setText(R.string.nc_client_cert_setup) } } if (finalAlias == null) { finalAlias = "" } - + Log.d(TAG, "host: $host and port: $port") currentUser!!.clientCertificate = finalAlias userManager.updateOrCreateUser(currentUser!!) }, @@ -406,12 +392,28 @@ class SettingsActivity : BaseActivity() { screenSecurityChangeListener = it } ) + var pos = getStringArray(R.array.screen_lock_timeout_entry_values).indexOf(appPreferences.screenLockTimeout) + binding.settingsScreenLockTimeoutLayoutDropdown.setText( + getStringArray(R.array.screen_lock_timeout_descriptions)[pos] + ) + binding.settingsScreenLockTimeoutLayoutDropdown.setSimpleItems(R.array.screen_lock_timeout_descriptions) + binding.settingsScreenLockTimeoutLayoutDropdown.setOnItemClickListener { _, _, position, _ -> + val entryVal: String = resources.getStringArray(R.array.screen_lock_timeout_entry_values)[position] + appPreferences.screenLockTimeout = entryVal + } appPreferences.registerScreenLockListener(ScreenLockListener().also { screenLockChangeListener = it }) appPreferences.registerScreenLockTimeoutListener( ScreenLockTimeoutListener().also { screenLockTimeoutChangeListener = it } ) + pos = getStringArray(R.array.theme_entry_values).indexOf(appPreferences.theme) + binding.settingsTheme.setText(getStringArray(R.array.theme_descriptions)[pos]) + binding.settingsTheme.setSimpleItems(R.array.theme_descriptions) + binding.settingsTheme.setOnItemClickListener { _, _, position, _ -> + val entryVal: String = getStringArray(R.array.theme_entry_values)[position] + appPreferences.theme = entryVal + } appPreferences.registerThemeChangeListener(ThemeChangeListener().also { themeChangeListener = it }) appPreferences.registerPhoneBookIntegrationChangeListener( PhoneBookIntegrationChangeListener(this).also { @@ -423,6 +425,9 @@ class SettingsActivity : BaseActivity() { readPrivacyChangeListener = it } ) + binding.settingsPrivacy.setOnClickListener { + readPrivacyChangeListener!!.onChanged(!binding.settingsReadPrivacySwitch.isChecked) + } appPreferences.registerTypingStatusChangeListener( TypingStatusChangeListener().also { typingStatusChangeListener = it @@ -465,7 +470,7 @@ class SettingsActivity : BaseActivity() { private fun removeCurrentAccount() { val otherUserExists = userManager.scheduleUserForDeletionWithId(currentUser!!.id!!).blockingGet() val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() - WorkManager.getInstance().enqueue(accountRemovalWork) + WorkManager.getInstance(this).enqueue(accountRemovalWork) if (otherUserExists) { // TODO: find better solution once Conductor is removed finish() @@ -488,33 +493,69 @@ class SettingsActivity : BaseActivity() { } } - // private fun themeSwitchPreferences() { - // binding.run { - // listOf( - // settingsScreenLock, - // settingsScreenSecurity, - // settingsIncognitoKeyboard, - // settingsPhoneBookIntegration, - // settingsReadPrivacy, - // settingsTypingStatus, - // settingsProxyUseCredentials - // ).forEach(viewThemeUtils.talk::colorSwitchPreference) - // } - // } - // - // private fun themeCategories() { - // binding.run { - // listOf( - // settingsNotificationsCategory, - // settingsAboutCategory, - // settingsAdvancedCategory, - // settingsAppearanceCategory, - // settingsPrivacyCategory - // ).forEach(viewThemeUtils.talk::colorPreferenceCategory) - // } - // } + private fun themeSwitchPreferences() { + binding.run { + listOf( + settingsScreenLockSwitch, + settingsScreenSecuritySwitch, + settingsIncognitoKeyboardSwitch, + settingsPhoneBookIntegrationSwitch, + settingsReadPrivacySwitch, + settingsTypingStatusSwitch, + settingsProxyUseCredentialsSwitch + ).forEach(viewThemeUtils.talk::colorSwitch) + } + } + + private fun themeTitles() { + binding.run { + listOf( + settingsNotificationsTitle, + settingsAboutTitle, + settingsAdvancedTitle, + settingsAppearanceTitle, + settingsPrivacyTitle + ).forEach(viewThemeUtils.platform::colorTextView) + } + } private fun setupProxyTypeSettings() { + if (appPreferences.proxyType == null) { + appPreferences.proxyType = resources.getString(R.string.nc_no_proxy) + } + binding.settingsProxyChoice.setText(appPreferences.proxyType) + binding.settingsProxyChoice.setSimpleItems(R.array.proxy_type_descriptions) + binding.settingsProxyChoice.setOnItemClickListener { _, _, position, _ -> + val entryVal = getStringArray(R.array.proxy_type_descriptions)[position] + appPreferences.proxyType = entryVal + } + + binding.settingsProxyHostEdit.setText(appPreferences.proxyHost) + binding.settingsProxyHostEdit.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + appPreferences.proxyHost = binding.settingsProxyHostEdit.text.toString() + } + } + + binding.settingsProxyPortEdit.setText(appPreferences.proxyPort) + binding.settingsProxyPortEdit.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + appPreferences.proxyPort = binding.settingsProxyPortEdit.text.toString() + } + } + binding.settingsProxyUsernameEdit.setText(appPreferences.proxyUsername) + binding.settingsProxyUsernameEdit.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + appPreferences.proxyUsername = binding.settingsProxyUsernameEdit.text.toString() + } + } + binding.settingsProxyPasswordEdit.setText(appPreferences.proxyPassword) + binding.settingsProxyPasswordEdit.setOnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + appPreferences.proxyPassword = binding.settingsProxyPasswordEdit.text.toString() + } + } + if (("No proxy" == appPreferences.proxyType) || appPreferences.proxyType == null) { hideProxySettings() } else { @@ -537,7 +578,7 @@ class SettingsActivity : BaseActivity() { binding.messageText.let { viewThemeUtils.platform.colorTextView(it, ColorRole.PRIMARY) it.text = resources!!.getString(R.string.nc_settings_account_updated) - binding.messageView.visibility = View.VISIBLE + binding.messageText.visibility = View.VISIBLE } } @@ -545,10 +586,10 @@ class SettingsActivity : BaseActivity() { binding.messageText.let { it.setTextColor(resources!!.getColor(R.color.nc_darkRed, null)) it.text = resources!!.getString(R.string.nc_settings_wrong_account) - binding.messageView.visibility = View.VISIBLE + binding.messageText.visibility = View.VISIBLE viewThemeUtils.platform.colorTextView(it, ColorRole.PRIMARY) it.text = resources!!.getString(R.string.nc_Server_account_imported) - binding.messageView.visibility = View.VISIBLE + binding.messageText.visibility = View.VISIBLE } } @@ -556,7 +597,7 @@ class SettingsActivity : BaseActivity() { binding.messageText.let { viewThemeUtils.platform.colorTextView(it, ColorRole.PRIMARY) it.text = resources!!.getString(R.string.nc_Server_account_imported) - binding.messageView.visibility = View.VISIBLE + binding.messageText.visibility = View.VISIBLE } } @@ -564,14 +605,14 @@ class SettingsActivity : BaseActivity() { binding.messageText.let { it.setTextColor(resources!!.getColor(R.color.nc_darkRed, null)) it.text = resources!!.getString(R.string.nc_server_failed_to_import_account) - binding.messageView.visibility = View.VISIBLE + binding.messageText.visibility = View.VISIBLE } } - else -> binding.messageView.visibility = View.GONE + else -> binding.messageText.visibility = View.GONE } ApplicationWideMessageHolder.getInstance().messageType = null - binding.messageView.animate() + binding.messageText.animate() ?.translationY(0f) ?.alpha(0.0f) ?.setDuration(DURATION) @@ -579,11 +620,11 @@ class SettingsActivity : BaseActivity() { ?.setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) - binding.messageView.visibility = View.GONE + binding.messageText.visibility = View.GONE } }) } else { - binding.messageView.visibility = View.GONE + binding.messageText.visibility = View.GONE } } @@ -611,7 +652,7 @@ class SettingsActivity : BaseActivity() { if ((!TextUtils.isEmpty(displayName) && !(displayName == currentUser!!.displayName))) { currentUser!!.displayName = displayName userManager.updateOrCreateUser(currentUser!!) - binding.displayNameText.text = currentUser!!.displayName + binding.nameText.text = currentUser!!.displayName } }, { dispose(profileQueryDisposable) }, @@ -648,42 +689,82 @@ class SettingsActivity : BaseActivity() { } private fun setupCheckables() { - (binding.settingsScreenSecurity.findViewById(R.id.mp_checkable) as Checkable).isChecked = - appPreferences.isScreenSecured + binding.settingsScreenSecuritySwitch.isChecked = appPreferences.isScreenSecured if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - (binding.settingsIncognitoKeyboard.findViewById(R.id.mp_checkable) as Checkable).isChecked = - appPreferences.isKeyboardIncognito + binding.settingsIncognitoKeyboardSwitch.isChecked = appPreferences.isKeyboardIncognito } - (binding.settingsIncognitoKeyboard.findViewById(R.id.mp_checkable) as Checkable).isChecked = - appPreferences.isKeyboardIncognito + binding.settingsIncognitoKeyboardSwitch.isChecked = appPreferences.isKeyboardIncognito if (CapabilitiesUtilNew.isReadStatusAvailable(currentUser!!)) { - (binding.settingsReadPrivacy.findViewById(R.id.mp_checkable) as Checkable).isChecked = - !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!) + binding.settingsReadPrivacySwitch.isChecked = !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!) } else { binding.settingsReadPrivacy.visibility = View.GONE } setupTypingStatusSetting() - (binding.settingsPhoneBookIntegration.findViewById(R.id.mp_checkable) as Checkable).isChecked = - appPreferences.isPhoneBookIntegrationEnabled + binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled + + binding.settingsProxyUseCredentialsSwitch.isChecked = appPreferences.proxyCredentials + binding.settingsProxyUseCredentials.setOnClickListener { + val isChecked = binding.settingsProxyUseCredentialsSwitch.isChecked + binding.settingsProxyUseCredentialsSwitch.isChecked = !isChecked + appPreferences.setProxyNeedsCredentials(!isChecked) + } + + binding.settingsScreenLockSwitch.isChecked = appPreferences.isScreenLocked + binding.settingsScreenLock.setOnClickListener { + val isChecked = binding.settingsScreenLockSwitch.isChecked + binding.settingsScreenLockSwitch.isChecked = !isChecked + appPreferences.setScreenLock(!isChecked) + } + + binding.settingsReadPrivacy.setOnClickListener { + val isChecked = binding.settingsReadPrivacySwitch.isChecked + binding.settingsReadPrivacySwitch.isChecked = !isChecked + appPreferences.setReadPrivacy(!isChecked) + } + + binding.settingsIncognitoKeyboard.setOnClickListener { + val isChecked = binding.settingsIncognitoKeyboardSwitch.isChecked + binding.settingsIncognitoKeyboardSwitch.isChecked = !isChecked + appPreferences.setIncognitoKeyboard(!isChecked) + } + + binding.settingsPhoneBookIntegration.setOnClickListener { + val isChecked = binding.settingsPhoneBookIntegrationSwitch.isChecked + binding.settingsPhoneBookIntegrationSwitch.isChecked = !isChecked + appPreferences.setPhoneBookIntegration(!isChecked) + } + + binding.settingsScreenSecurity.setOnClickListener { + val isChecked = binding.settingsScreenSecuritySwitch.isChecked + binding.settingsScreenSecuritySwitch.isChecked = !isChecked + appPreferences.setScreenSecurity(!isChecked) + } + + binding.settingsTypingStatus.setOnClickListener { + val isChecked = binding.settingsTypingStatusSwitch.isChecked + binding.settingsTypingStatusSwitch.isChecked = !isChecked + appPreferences.setTypingStatus(!isChecked) + } } private fun setupTypingStatusSetting() { if (currentUser!!.externalSignalingServer?.externalSignalingServer?.isNotEmpty() == true) { binding.settingsTypingStatusOnlyWithHpb.visibility = View.GONE + Log.i(TAG, "Typing Status Available: ${CapabilitiesUtilNew.isTypingStatusAvailable(currentUser!!)}") if (CapabilitiesUtilNew.isTypingStatusAvailable(currentUser!!)) { - (binding.settingsTypingStatus.findViewById(R.id.mp_checkable) as Checkable).isChecked = - !CapabilitiesUtilNew.isTypingStatusPrivate(currentUser!!) + binding.settingsTypingStatusSwitch.isChecked = !CapabilitiesUtilNew.isTypingStatusPrivate(currentUser!!) } else { binding.settingsTypingStatus.visibility = View.GONE } } else { - (binding.settingsTypingStatus.findViewById(R.id.mp_checkable) as Checkable).isChecked = false + Log.i(TAG, "Typing Status not Available") + binding.settingsTypingStatusSwitch.isChecked = false binding.settingsTypingStatusOnlyWithHpb.visibility = View.VISIBLE binding.settingsTypingStatus.isEnabled = false binding.settingsTypingStatusOnlyWithHpb.alpha = DISABLED_ALPHA @@ -696,9 +777,8 @@ class SettingsActivity : BaseActivity() { if (keyguardManager.isKeyguardSecure) { binding.settingsScreenLock.isEnabled = true binding.settingsScreenLockTimeout.isEnabled = true - (binding.settingsScreenLock.findViewById(R.id.mp_checkable) as Checkable).isChecked = - appPreferences.isScreenLocked - binding.settingsScreenLockTimeout.isEnabled = appPreferences.isScreenLocked + binding.settingsScreenLockSwitch.isChecked = appPreferences.isScreenLocked + binding.settingsScreenLockTimeoutLayoutDropdown.isEnabled = appPreferences.isScreenLocked if (appPreferences.isScreenLocked) { binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA } else { @@ -707,10 +787,10 @@ class SettingsActivity : BaseActivity() { binding.settingsScreenLock.alpha = ENABLED_ALPHA } else { binding.settingsScreenLock.isEnabled = false - binding.settingsScreenLockTimeout.isEnabled = false + binding.settingsScreenLockTimeoutLayoutDropdown.isEnabled = false appPreferences.removeScreenLock() appPreferences.removeScreenLockTimeout() - (binding.settingsScreenLock.findViewById(R.id.mp_checkable) as Checkable).isChecked = false + binding.settingsScreenLockSwitch.isChecked = false binding.settingsScreenLock.alpha = DISABLED_ALPHA binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA } @@ -736,35 +816,35 @@ class SettingsActivity : BaseActivity() { appPreferences.removeProxyCredentials() appPreferences.removeProxyUsername() appPreferences.removeProxyPassword() - binding.settingsScreen.findViewById(R.id.settings_proxy_host_edit)?.visibility = View.GONE - binding.settingsScreen.findViewById(R.id.settings_proxy_port_edit)?.visibility = View.GONE - binding.settingsScreen.findViewById(R.id.settings_proxy_use_credentials)?.visibility = + binding.settingsProxyHostLayout.visibility = View.GONE + binding.settingsProxyPortLayout.visibility = View.GONE + binding.settingsProxyUseCredentials.visibility = View.GONE - binding.settingsScreen.findViewById(R.id.settings_proxy_username_edit)?.visibility = View.GONE - binding.settingsScreen.findViewById(R.id.settings_proxy_password_edit)?.visibility = View.GONE + hideProxyCredentials() } private fun showProxySettings() { - binding.settingsScreen.findViewById(R.id.settings_proxy_host_edit)?.visibility = + binding.settingsProxyHostLayout.visibility = View.VISIBLE - binding.settingsScreen.findViewById(R.id.settings_proxy_port_edit)?.visibility = + binding.settingsProxyPortLayout.visibility = View.VISIBLE - binding.settingsScreen.findViewById(R.id.settings_proxy_use_credentials)?.visibility = + binding.settingsProxyUseCredentials.visibility = View.VISIBLE + if (binding.settingsProxyUseCredentialsSwitch.isChecked) showProxyCredentials() } private fun showProxyCredentials() { - binding.settingsScreen.findViewById(R.id.settings_proxy_username_edit)?.visibility = + binding.settingsProxyUsernameLayout.visibility = View.VISIBLE - binding.settingsScreen.findViewById(R.id.settings_proxy_password_edit)?.visibility = + binding.settingsProxyPasswordLayout.visibility = View.VISIBLE } private fun hideProxyCredentials() { appPreferences.removeProxyUsername() appPreferences.removeProxyPassword() - binding.settingsScreen.findViewById(R.id.settings_proxy_username_edit)?.visibility = View.GONE - binding.settingsScreen.findViewById(R.id.settings_proxy_password_edit)?.visibility = View.GONE + binding.settingsProxyUsernameLayout.visibility = View.GONE + binding.settingsProxyPasswordLayout.visibility = View.GONE } private fun dispose(disposable: Disposable?) { @@ -801,13 +881,12 @@ class SettingsActivity : BaseActivity() { grantResults[0] == PackageManager.PERMISSION_GRANTED ) { WorkManager - .getInstance() + .getInstance(this) .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java).build()) checkForPhoneNumber() } else { appPreferences.setPhoneBookIntegration(false) - (binding.settingsPhoneBookIntegration.findViewById(R.id.mp_checkable) as Checkable).isChecked = - appPreferences.isPhoneBookIntegrationEnabled + binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled Toast.makeText( context, context.resources.getString(R.string.no_phone_book_integration_due_to_permissions), @@ -860,15 +939,19 @@ class SettingsActivity : BaseActivity() { hideProxySettings() } else { when (newValue) { - "HTTP" -> - binding.settingsProxyPortEdit.value = "3128" - - "DIRECT" -> - binding.settingsProxyPortEdit.value = "8080" - - "SOCKS" -> - binding.settingsProxyPortEdit.value = "1080" + "HTTP" -> { + binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_http_value)) + appPreferences.proxyPort = "3128" + } + "DIRECT" -> { + binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_direct_value)) + appPreferences.proxyPort = "8080" + } + "SOCKS" -> { + binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_socks_value)) + appPreferences.proxyPort = "1080" + } else -> { } } @@ -1033,7 +1116,7 @@ class SettingsActivity : BaseActivity() { ncApi.setReadStatusPrivacy( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), - RequestBody.create("application/json".toMediaTypeOrNull(), json) + json.toRequestBody("application/json".toMediaTypeOrNull()) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1048,8 +1131,7 @@ class SettingsActivity : BaseActivity() { override fun onError(e: Throwable) { appPreferences.setReadPrivacy(!newValue) - (binding.settingsReadPrivacy.findViewById(R.id.mp_checkable) as Checkable).isChecked = - !newValue + binding.settingsReadPrivacySwitch.isChecked = !newValue } override fun onComplete() { @@ -1066,7 +1148,7 @@ class SettingsActivity : BaseActivity() { ncApi.setTypingStatusPrivacy( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), - RequestBody.create("application/json".toMediaTypeOrNull(), json) + json.toRequestBody("application/json".toMediaTypeOrNull()) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1076,13 +1158,13 @@ class SettingsActivity : BaseActivity() { } override fun onNext(genericOverall: GenericOverall) { - // unused atm + loadCapabilitiesAndUpdateSettings() + Log.i(TAG, "onNext called typing status set") } override fun onError(e: Throwable) { appPreferences.setTypingStatus(!newValue) - (binding.settingsTypingStatus.findViewById(R.id.mp_checkable) as Checkable).isChecked = - !newValue + binding.settingsTypingStatusSwitch.isChecked = !newValue } override fun onComplete() { diff --git a/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt b/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt index 0828ba3a8..039012c53 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt @@ -49,7 +49,6 @@ import com.nextcloud.talk.R import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DrawableUtils import com.vanniktech.emoji.EmojiTextView -import com.yarolegovich.mp.MaterialPreferenceCategory import javax.inject.Inject import kotlin.math.roundToInt @@ -139,17 +138,9 @@ class TalkSpecificViewThemeUtils @Inject constructor( } } - fun colorPreferenceCategory(category: MaterialPreferenceCategory) { - withScheme(category) { scheme -> - category.setTitleColor(scheme.primary) - } - } - fun colorSwitch(preference: MaterialSwitch) { val switch = preference as SwitchCompat - if (switch != null) { - appcompat.colorSwitchCompat(switch) - } + appcompat.colorSwitchCompat(switch) } fun setCheckedBackground(emoji: EmojiTextView) { @@ -252,35 +243,6 @@ class TalkSpecificViewThemeUtils @Inject constructor( } } - fun ConversationInfoCardView(cardView: MaterialCardView) { - withScheme(cardView) { scheme -> - val background = cardView.context.getColor(R.color.bg_default) - cardView.backgroundTintList = - ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - scheme.primary, - background - ) - ) - cardView.setStrokeColor( - ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - scheme.primary, - background - ) - ) - ) - } - } - companion object { private val THEMEABLE_PLACEHOLDER_IDS = listOf( R.drawable.ic_mimetype_package_x_generic, @@ -290,6 +252,5 @@ class TalkSpecificViewThemeUtils @Inject constructor( private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt() private const val HALF_ALPHA_INT: Int = 255 / 2 - private const val SEARCH_TEXT_SIZE: Float = 16f } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java index 1170a5863..62cdd084d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java @@ -24,6 +24,8 @@ package com.nextcloud.talk.utils.preferences; +import android.annotation.SuppressLint; + import com.nextcloud.talk.R; import net.orange_box.storebox.annotations.method.ClearMethod; @@ -37,7 +39,7 @@ import net.orange_box.storebox.annotations.option.SaveOption; import net.orange_box.storebox.enums.SaveMode; import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener; - +@SuppressLint("NonConstantResourceId") @SaveOption(SaveMode.APPLY) public interface AppPreferences { @@ -253,7 +255,7 @@ public interface AppPreferences { String getScreenLockTimeout(); @KeyByString("screen_lock_timeout") - void setScreenLockTimeout(int value); + void setScreenLockTimeout(String value); @KeyByString("screen_lock_timeout") @RemoveMethod @@ -271,6 +273,9 @@ public interface AppPreferences { @DefaultValue(R.string.nc_default_theme) String getTheme(); + @KeyByResource(R.string.nc_settings_theme_key) + void setTheme(String newValue); + @KeyByResource(R.string.nc_settings_theme_key) @RemoveMethod void removeTheme(); diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java deleted file mode 100644 index b91e4e287..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.utils.preferences; - -import android.app.Dialog; -import android.content.Context; -import android.os.Build; -import android.text.InputType; -import android.view.LayoutInflater; -import android.view.View; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; -import android.widget.TextView; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.nextcloud.talk.R; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.ui.theme.ViewThemeUtils; -import com.yarolegovich.mp.io.StandardUserInputModule; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import autodagger.AutoInjector; - -@AutoInjector(NextcloudTalkApplication.class) -public class MagicUserInputModule extends StandardUserInputModule { - - @Inject - AppPreferences appPreferences; - - @Inject - ViewThemeUtils viewThemeUtils; - - private List keysWithIntegerInput = new ArrayList<>(); - - public MagicUserInputModule(Context context) { - super(context); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - } - - public MagicUserInputModule(Context context, List keysWithIntegerInput) { - super(context); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - this.keysWithIntegerInput = keysWithIntegerInput; - } - - @Override - public void showEditTextInput( - String key, - CharSequence title, - CharSequence defaultValue, - final Listener listener) { - final View view = LayoutInflater.from(context).inflate(R.layout.dialog_edittext, null); - final EditText inputField = view.findViewById(R.id.mp_text_input); - viewThemeUtils.platform.colorEditText(inputField); - - int paddingStartEnd = Math.round(view.getResources().getDimension(R.dimen.standard_padding)); - int paddingTopBottom = Math.round(view.getResources().getDimension(R.dimen.dialog_padding_top_bottom)); - view.setPadding(paddingStartEnd, paddingTopBottom, paddingStartEnd, paddingTopBottom); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) { - inputField.setImeOptions(inputField.getImeOptions() | EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING); - } - - if (defaultValue != null) { - inputField.setText(defaultValue); - inputField.setSelection(defaultValue.length()); - } - - if (keysWithIntegerInput.contains(key)) { - inputField.setInputType(InputType.TYPE_CLASS_NUMBER); - } - - final MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(view.getContext()) - .setTitle(title) - .setView(view); - - viewThemeUtils.dialog.colorMaterialAlertDialogBackground(view.getContext(), dialogBuilder); - - final Dialog dialog = dialogBuilder.show(); - - TextView button = view.findViewById(R.id.mp_btn_confirm); - viewThemeUtils.platform.colorPrimaryTextViewElement(button); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - listener.onInput(inputField.getText().toString()); - dialog.dismiss(); - } - }); - } - -} diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java index 4ad73d443..cc4365c55 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java @@ -159,7 +159,7 @@ public class DatabaseStorageModule { } else if ("conversation_info_message_notifications_dropdown".equals(key)) { if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { - if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) { + if (TextUtils.isEmpty(messageNotificationLevel) || !messageNotificationLevel.equals(value)) { int intValue; switch (value) { case "never": @@ -191,10 +191,7 @@ public class DatabaseStorageModule { } @Override - public void onNext(GenericOverall genericOverall) { - Log.i(TAG, "onNext called"); - messageNotificationLevel = value; - } + public void onNext(GenericOverall genericOverall) {messageNotificationLevel = value;} @Override public void onError(Throwable e) { diff --git a/app/src/main/res/layout/activity_conversation_info.xml b/app/src/main/res/layout/activity_conversation_info.xml index 4fc4b4c1f..8e35f8552 100644 --- a/app/src/main/res/layout/activity_conversation_info.xml +++ b/app/src/main/res/layout/activity_conversation_info.xml @@ -75,18 +75,14 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - - @@ -111,9 +107,7 @@ - - - - + - - - - - - - - - - - - + android:padding="@dimen/standard_padding" + android:background="?android:attr/selectableItemBackground"> - + - - - - - - @@ -290,9 +252,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/standard_half_margin" - android:layout_marginHorizontal="@dimen/standard_half_margin" - android:hint="@string/nc_expire_messages" - app:boxStrokeWidth="@dimen/zero"> + android:layout_marginHorizontal="@dimen/standard_margin" + android:hint="@string/nc_expire_messages"> - - + android:textColor="@color/disabled_text" + android:textSize="@dimen/supporting_text_text_size"/> - - - - - - - - + android:padding="@dimen/standard_padding" + android:background="?android:attr/selectableItemBackground"> - + - - - - - - @@ -402,140 +344,93 @@ android:layout_height="wrap_content" android:text="@string/danger_zone" android:textColor="@color/design_default_color_error" - android:textSize="@dimen/md_title_textsize" + android:textSize="@dimen/headline_text_size" android:textStyle="bold" android:padding="@dimen/standard_padding"/> - - - + android:padding="@dimen/standard_padding" + android:background="?android:attr/selectableItemBackground"> - + - - - + - - - + - - - + android:padding="@dimen/standard_padding" + android:background="?android:attr/selectableItemBackground"> - + - + + - - - - - - - - - - - - + android:padding="@dimen/standard_padding" + android:background="?android:attr/selectableItemBackground"> - + - - - - - - - - - - + + - - - diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 953a97166..61bcec01b 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,40 +1,18 @@ - - - + + + - + android:layout_height="wrap_content"> - - + android:layout_height="wrap_content" + android:orientation="vertical"> - - - - - + android:gravity="center" + android:text="@string/this_is_a_test_message"/> - + android:layout_marginTop="@dimen/standard_quarter_margin" + android:animateLayoutChanges="true" + android:visibility="gone" + tools:visibility="visible" + android:layout_marginStart="@dimen/standard_margin" + android:layout_marginEnd="@dimen/standard_margin" + android:background="?android:attr/selectableItemBackground"> + + + + + + + + - - - - + android:orientation="horizontal" + android:background="?android:attr/selectableItemBackground"> - + - + - + - + - + - + - - + - + - + - + - + - + - + + - + - + - + - - - + + - + - + - + - + - + - + + - + + - + - - + - + - - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/guest_access_settings_item.xml b/app/src/main/res/layout/guest_access_settings_item.xml deleted file mode 100644 index 6a82431f1..000000000 --- a/app/src/main/res/layout/guest_access_settings_item.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_guest_access_settings.xml b/app/src/main/res/layout/item_guest_access_settings.xml index aac1979b4..348d9bac2 100644 --- a/app/src/main/res/layout/item_guest_access_settings.xml +++ b/app/src/main/res/layout/item_guest_access_settings.xml @@ -2,21 +2,11 @@ - - - - - - + android:layout_height="wrap_content" + android:paddingStart="@dimen/standard_margin" + android:paddingTop="@dimen/standard_margin" + android:paddingEnd="@dimen/standard_margin" + android:paddingBottom="@dimen/standard_half_margin" + android:orientation="horizontal" + android:background="?android:attr/selectableItemBackground"> + android:layout_marginStart="@dimen/standard_margin" + android:clickable="false" /> - - - + android:background="?android:attr/selectableItemBackground"> + android:layout_marginStart="@dimen/standard_margin" + android:clickable="false" /> - - - - - - - - - + android:background="?android:attr/selectableItemBackground"> + + + + + - - - - - - - - - - + android:background="?android:attr/selectableItemBackground"> + + + + + - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_notification_settings.xml b/app/src/main/res/layout/item_notification_settings.xml index 5b3309fe7..872e4e439 100644 --- a/app/src/main/res/layout/item_notification_settings.xml +++ b/app/src/main/res/layout/item_notification_settings.xml @@ -1,21 +1,11 @@ - - - - - - + android:textStyle="bold" + android:textSize="@dimen/headline_text_size"/> + android:orientation="horizontal" + android:background="?android:attr/selectableItemBackground"> + android:layout_marginStart="@dimen/standard_margin" + android:clickable="false" /> - + android:hint="@string/nc_plain_old_messages"> - - - - + android:paddingStart="@dimen/standard_margin" + android:paddingEnd="@dimen/standard_margin" + android:background="?android:attr/selectableItemBackground"> - + android:checked="true" + android:clickable="false" /> - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_webinar_info.xml b/app/src/main/res/layout/item_webinar_info.xml index 5ba578532..b72ec7a98 100644 --- a/app/src/main/res/layout/item_webinar_info.xml +++ b/app/src/main/res/layout/item_webinar_info.xml @@ -1,20 +1,11 @@ - - - - - + android:textStyle="bold" + android:textSize="@dimen/headline_text_size"/> + android:layout_height="wrap_content" + android:paddingStart="@dimen/standard_margin" + android:paddingEnd="@dimen/standard_margin" + android:orientation="horizontal" + android:background="?android:attr/selectableItemBackground"> @@ -73,25 +62,22 @@ android:layout_gravity="center_vertical" android:layout_marginStart="@dimen/standard_margin" android:layout_marginEnd="1dp" - android:checked="true" /> + android:checked="true" + android:clickable="false" /> - - - + android:orientation="horizontal" + android:background="?android:attr/selectableItemBackground"> - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/notification_settings_item.xml b/app/src/main/res/layout/notification_settings_item.xml deleted file mode 100644 index 8551de884..000000000 --- a/app/src/main/res/layout/notification_settings_item.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/webinar_info_item.xml b/app/src/main/res/layout/webinar_info_item.xml deleted file mode 100644 index f0ff2bdf8..000000000 --- a/app/src/main/res/layout/webinar_info_item.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index d5c617524..89956198c 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -91,5 +91,6 @@ 40dp 30dp + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 382293b4a..2335b3039 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -153,6 +153,7 @@ How to translate with transifex: Read status Share my typing-status and show the typing-status of others Typing status + Typing status is only available when using a high performance backend (HPB) 30 seconds 1 minute @@ -196,6 +197,7 @@ How to translate with transifex: New conversation Join with a link + List open conversations Join via web Mark as read Mark as unread @@ -204,6 +206,10 @@ How to translate with transifex: Forward to … + + No open conversations + No open conversations that you can join.\nEither there are no open conversations or you already joined all of them. + Select participants Add participants @@ -559,6 +565,14 @@ How to translate with transifex: Invalid password Do you want to reauthorize or delete this account? + App is outdated + The app is too old and no longer supported by this server. Please update. + Update + Switch account + Maintenance mode + Server is currently in maintenance mode. + Close app + Take a photo Switch camera @@ -672,5 +686,13 @@ How to translate with transifex: Could not detect language Copy translated text Danger Zone + Filter Conversations + Mentioned + Unread + 3128 + 8080 + 1080 + This is a test message + v0.1