() {
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginWebBinding {
+ return FragmentLoginWebBinding.inflate(inflater, container, false)
+ }
+
+ private var isWebViewLoaded = false
+ private var isForSessionRecovery = false
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupToolbar(views.loginWebToolbar)
+ }
+
+ override fun updateWithState(state: LoginViewState2) {
+ setupTitle(state)
+
+ isForSessionRecovery = state.deviceId?.isNotBlank() == true
+
+ if (!isWebViewLoaded) {
+ setupWebView(state)
+ isWebViewLoaded = true
+ }
+ }
+
+ private fun setupTitle(state: LoginViewState2) {
+ views.loginWebToolbar.title = when (state.signMode) {
+ SignMode2.SignIn -> getString(R.string.login_signin)
+ else -> getString(R.string.login_signup)
+ }
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ private fun setupWebView(state: LoginViewState2) {
+ views.loginWebWebView.settings.javaScriptEnabled = true
+
+ // Enable local storage to support SSO with Firefox accounts
+ views.loginWebWebView.settings.domStorageEnabled = true
+ views.loginWebWebView.settings.databaseEnabled = true
+
+ // Due to https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html, we hack
+ // the user agent to bypass the limitation of Google, as a quick fix (a proper solution will be to use the SSO SDK)
+ views.loginWebWebView.settings.userAgentString = "Mozilla/5.0 Google"
+
+ // AppRTC requires third party cookies to work
+ val cookieManager = android.webkit.CookieManager.getInstance()
+
+ // clear the cookies
+ if (cookieManager == null) {
+ launchWebView(state)
+ } else {
+ if (!cookieManager.hasCookies()) {
+ launchWebView(state)
+ } else {
+ try {
+ cookieManager.removeAllCookies { launchWebView(state) }
+ } catch (e: Exception) {
+ Timber.e(e, " cookieManager.removeAllCookie() fails")
+ launchWebView(state)
+ }
+ }
+ }
+ }
+
+ private fun launchWebView(state: LoginViewState2) {
+ val url = loginViewModel.getFallbackUrl(state.signMode == SignMode2.SignIn, state.deviceId) ?: return
+
+ views.loginWebWebView.loadUrl(url)
+
+ views.loginWebWebView.webViewClient = object : WebViewClient() {
+ override fun onReceivedSslError(view: WebView, handler: SslErrorHandler,
+ error: SslError) {
+ AlertDialog.Builder(requireActivity())
+ .setMessage(R.string.ssl_could_not_verify)
+ .setPositiveButton(R.string.ssl_trust) { _, _ -> handler.proceed() }
+ .setNegativeButton(R.string.ssl_do_not_trust) { _, _ -> handler.cancel() }
+ .setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
+ if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+ handler.cancel()
+ dialog.dismiss()
+ return@OnKeyListener true
+ }
+ false
+ })
+ .setCancelable(false)
+ .show()
+ }
+
+ override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
+ super.onReceivedError(view, errorCode, description, failingUrl)
+
+ loginViewModel.handle(LoginAction2.PostViewEvent(LoginViewEvents2.OnWebLoginError(errorCode, description, failingUrl)))
+ }
+
+ override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
+ super.onPageStarted(view, url, favicon)
+
+ views.loginWebToolbar.subtitle = url
+ }
+
+ override fun onPageFinished(view: WebView, url: String) {
+ // avoid infinite onPageFinished call
+ if (url.startsWith("http")) {
+ // Generic method to make a bridge between JS and the UIWebView
+ assetReader.readAssetFile("sendObject.js")?.let { view.loadUrl(it) }
+
+ if (state.signMode == SignMode2.SignIn) {
+ // The function the fallback page calls when the login is complete
+ assetReader.readAssetFile("onLogin.js")?.let { view.loadUrl(it) }
+ } else {
+ // MODE_REGISTER
+ // The function the fallback page calls when the registration is complete
+ assetReader.readAssetFile("onRegistered.js")?.let { view.loadUrl(it) }
+ }
+ }
+ }
+
+ /**
+ * Example of (formatted) url for MODE_LOGIN:
+ *
+ *
+ * js:{
+ * "action":"onLogin",
+ * "credentials":{
+ * "user_id":"@user:matrix.org",
+ * "access_token":"[ACCESS_TOKEN]",
+ * "home_server":"matrix.org",
+ * "device_id":"[DEVICE_ID]",
+ * "well_known":{
+ * "m.homeserver":{
+ * "base_url":"https://matrix.org/"
+ * }
+ * }
+ * }
+ * }
+ *
+ * @param view
+ * @param url
+ * @return
+ */
+ override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
+ if (url == null) return super.shouldOverrideUrlLoading(view, url as String?)
+
+ if (url.startsWith("js:")) {
+ var json = url.substring(3)
+ var javascriptResponse: JavascriptResponse? = null
+
+ try {
+ // URL decode
+ json = URLDecoder.decode(json, "UTF-8")
+ val adapter = MoshiProvider.providesMoshi().adapter(JavascriptResponse::class.java)
+ javascriptResponse = adapter.fromJson(json)
+ } catch (e: Exception) {
+ Timber.e(e, "## shouldOverrideUrlLoading() : fromJson failed")
+ }
+
+ // succeeds to parse parameters
+ if (javascriptResponse != null) {
+ val action = javascriptResponse.action
+
+ if (state.signMode == SignMode2.SignIn) {
+ if (action == "onLogin") {
+ javascriptResponse.credentials?.let { notifyViewModel(it) }
+ }
+ } else {
+ // MODE_REGISTER
+ // check the required parameters
+ if (action == "onRegistered") {
+ javascriptResponse.credentials?.let { notifyViewModel(it) }
+ }
+ }
+ }
+ return true
+ }
+
+ return super.shouldOverrideUrlLoading(view, url)
+ }
+ }
+ }
+
+ private fun notifyViewModel(credentials: Credentials) {
+ if (isForSessionRecovery) {
+ val softLogoutViewModel: SoftLogoutViewModel by activityViewModel()
+ softLogoutViewModel.handle(SoftLogoutAction.WebLoginSuccess(credentials))
+ } else {
+ loginViewModel.handle(LoginAction2.WebLoginSuccess(credentials))
+ }
+ }
+
+ override fun resetViewModel() {
+ loginViewModel.handle(LoginAction2.ResetSignin)
+ }
+
+ override fun onBackPressed(toolbarButton: Boolean): Boolean {
+ return when {
+ toolbarButton -> super.onBackPressed(toolbarButton)
+ views.loginWebWebView.canGoBack() -> views.loginWebWebView.goBack().run { true }
+ else -> super.onBackPressed(toolbarButton)
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/SignMode2.kt b/vector/src/main/java/im/vector/app/features/login2/SignMode2.kt
new file mode 100644
index 0000000000..f3d59837e7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/SignMode2.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2
+
+enum class SignMode2 {
+ Unknown,
+
+ // Account creation
+ SignUp,
+
+ // Login
+ SignIn
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedAction.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedAction.kt
new file mode 100644
index 0000000000..f108bfa886
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedAction.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2.created
+
+import android.net.Uri
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed class AccountCreatedAction : VectorViewModelAction {
+ data class SetDisplayName(val displayName: String) : AccountCreatedAction()
+ data class SetAvatar(val avatarUri: Uri, val filename: String) : AccountCreatedAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt
new file mode 100644
index 0000000000..861e3f9e98
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2.created
+
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.app.R
+import im.vector.app.core.date.DateFormatKind
+import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
+import im.vector.app.core.intent.getFilenameFromUri
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.databinding.DialogBaseEditTextBinding
+import im.vector.app.databinding.FragmentLoginAccountCreatedBinding
+import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
+import im.vector.app.features.login2.AbstractLoginFragment2
+import im.vector.app.features.login2.LoginAction2
+import im.vector.app.features.login2.LoginActivity2
+import im.vector.app.features.login2.LoginViewState2
+import org.matrix.android.sdk.api.util.MatrixItem
+import java.util.UUID
+import javax.inject.Inject
+
+/**
+ * In this screen:
+ * - the account has been created and we propose the user to set an avatar and a display name
+ */
+class AccountCreatedFragment @Inject constructor(
+ val accountCreatedViewModelFactory: AccountCreatedViewModel.Factory,
+ private val avatarRenderer: AvatarRenderer,
+ private val dateFormatter: VectorDateFormatter,
+ private val matrixItemColorProvider: MatrixItemColorProvider,
+ colorProvider: ColorProvider
+) : AbstractLoginFragment2(),
+ GalleryOrCameraDialogHelper.Listener {
+
+ private val viewModel: AccountCreatedViewModel by fragmentViewModel()
+
+ private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginAccountCreatedBinding {
+ return FragmentLoginAccountCreatedBinding.inflate(inflater, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupClickListener()
+ setupSubmitButton()
+ observeViewEvents()
+
+ viewModel.subscribe { invalidateState(it) }
+
+ views.loginAccountCreatedTime.text = dateFormatter.format(System.currentTimeMillis(), DateFormatKind.MESSAGE_SIMPLE)
+ }
+
+ private fun observeViewEvents() {
+ viewModel.observeViewEvents {
+ when (it) {
+ is AccountCreatedViewEvents.Failure -> displayErrorDialog(it.throwable)
+ }
+ }
+ }
+
+ private fun setupClickListener() {
+ views.loginAccountCreatedMessage.setOnClickListener {
+ // Update display name
+ displayDialog()
+ }
+ views.loginAccountCreatedAvatar.setOnClickListener {
+ galleryOrCameraDialogHelper.show()
+ }
+ }
+
+ private fun displayDialog() = withState(viewModel) { state ->
+ val inflater = requireActivity().layoutInflater
+ val layout = inflater.inflate(R.layout.dialog_base_edit_text, null)
+ val views = DialogBaseEditTextBinding.bind(layout)
+ views.editText.setText(state.currentUser()?.getBestName().orEmpty())
+
+ AlertDialog.Builder(requireActivity())
+ .setTitle(R.string.settings_display_name)
+ .setView(layout)
+ .setPositiveButton(R.string.ok) { _, _ ->
+ val newName = views.editText.text.toString()
+ viewModel.handle(AccountCreatedAction.SetDisplayName(newName))
+ }
+ .setNegativeButton(R.string.cancel, null)
+ .show()
+ }
+
+ override fun onImageReady(uri: Uri?) {
+ uri ?: return
+ viewModel.handle(AccountCreatedAction.SetAvatar(
+ avatarUri = uri,
+ filename = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString())
+ )
+ }
+
+ private fun setupSubmitButton() {
+ views.loginAccountCreatedLater.setOnClickListener { terminate() }
+ views.loginAccountCreatedDone.setOnClickListener { terminate() }
+ }
+
+ private fun terminate() {
+ loginViewModel.handle(LoginAction2.Finish)
+ }
+
+ private fun invalidateState(state: AccountCreatedViewState) {
+ // Ugly hack...
+ (activity as? LoginActivity2)?.setIsLoading(state.isLoading)
+
+ views.loginAccountCreatedSubtitle.text = getString(R.string.login_account_created_subtitle, state.userId)
+
+ val user = state.currentUser()
+ if (user != null) {
+ avatarRenderer.render(user, views.loginAccountCreatedAvatar)
+ views.loginAccountCreatedMemberName.text = user.getBestName()
+ } else {
+ // Should not happen
+ views.loginAccountCreatedMemberName.text = state.userId
+ }
+
+ // User color
+ views.loginAccountCreatedMemberName
+ .setTextColor(matrixItemColorProvider.getColor(MatrixItem.UserItem(state.userId)))
+
+ views.loginAccountCreatedLater.isVisible = state.hasBeenModified.not()
+ views.loginAccountCreatedDone.isVisible = state.hasBeenModified
+ }
+
+ override fun updateWithState(state: LoginViewState2) {
+ // No op
+ }
+
+ override fun resetViewModel() {
+ // No op
+ }
+
+ override fun onBackPressed(toolbarButton: Boolean): Boolean {
+ // Just start the next Activity
+ terminate()
+ return false
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewEvents.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewEvents.kt
new file mode 100644
index 0000000000..4677e1abd5
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewEvents.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package im.vector.app.features.login2.created
+
+import im.vector.app.core.platform.VectorViewEvents
+
+/**
+ * Transient events for Account Created
+ */
+sealed class AccountCreatedViewEvents : VectorViewEvents {
+ data class Failure(val throwable: Throwable) : AccountCreatedViewEvents()
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt
new file mode 100644
index 0000000000..1acec968b6
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2.created
+
+import androidx.lifecycle.viewModelScope
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.ViewModelContext
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.platform.VectorViewModel
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.MatrixPatterns
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.util.MatrixItem
+import org.matrix.android.sdk.api.util.toMatrixItem
+import org.matrix.android.sdk.rx.rx
+import org.matrix.android.sdk.rx.unwrap
+import timber.log.Timber
+
+class AccountCreatedViewModel @AssistedInject constructor(
+ @Assisted initialState: AccountCreatedViewState,
+ private val session: Session
+) : VectorViewModel(initialState) {
+
+ @AssistedFactory
+ interface Factory {
+ fun create(initialState: AccountCreatedViewState): AccountCreatedViewModel
+ }
+
+ companion object : MvRxViewModelFactory {
+
+ @JvmStatic
+ override fun create(viewModelContext: ViewModelContext, state: AccountCreatedViewState): AccountCreatedViewModel? {
+ val fragment: AccountCreatedFragment = (viewModelContext as FragmentViewModelContext).fragment()
+ return fragment.accountCreatedViewModelFactory.create(state)
+ }
+ }
+
+ init {
+ setState {
+ copy(
+ userId = session.myUserId
+ )
+ }
+ observeUser()
+ }
+
+ private fun observeUser() {
+ session.rx()
+ .liveUser(session.myUserId)
+ .unwrap()
+ .map {
+ if (MatrixPatterns.isUserId(it.userId)) {
+ it.toMatrixItem()
+ } else {
+ Timber.w("liveUser() has returned an invalid user: $it")
+ MatrixItem.UserItem(session.myUserId, null, null)
+ }
+ }
+ .execute {
+ copy(currentUser = it)
+ }
+ }
+
+ override fun handle(action: AccountCreatedAction) {
+ when (action) {
+ is AccountCreatedAction.SetAvatar -> handleSetAvatar(action)
+ is AccountCreatedAction.SetDisplayName -> handleSetDisplayName(action)
+ }
+ }
+
+ private fun handleSetAvatar(action: AccountCreatedAction.SetAvatar) {
+ setState { copy(isLoading = true) }
+ viewModelScope.launch {
+ val result = runCatching { session.updateAvatar(session.myUserId, action.avatarUri, action.filename) }
+ .onFailure { _viewEvents.post(AccountCreatedViewEvents.Failure(it)) }
+ setState {
+ copy(
+ isLoading = false,
+ hasBeenModified = hasBeenModified || result.isSuccess
+ )
+ }
+ }
+ }
+
+ private fun handleSetDisplayName(action: AccountCreatedAction.SetDisplayName) {
+ setState { copy(isLoading = true) }
+ viewModelScope.launch {
+ val result = runCatching { session.setDisplayName(session.myUserId, action.displayName) }
+ .onFailure { _viewEvents.post(AccountCreatedViewEvents.Failure(it)) }
+ setState {
+ copy(
+ isLoading = false,
+ hasBeenModified = hasBeenModified || result.isSuccess
+ )
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt
new file mode 100644
index 0000000000..80211b3da2
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewState.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2.created
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.Uninitialized
+import org.matrix.android.sdk.api.util.MatrixItem
+
+data class AccountCreatedViewState(
+ val userId: String = "",
+ val isLoading: Boolean = false,
+ val currentUser: Async = Uninitialized,
+ val hasBeenModified: Boolean = false
+) : MvRxState
diff --git a/vector/src/main/java/im/vector/app/features/login2/terms/LoginTermsFragment2.kt b/vector/src/main/java/im/vector/app/features/login2/terms/LoginTermsFragment2.kt
new file mode 100755
index 0000000000..0be696e1c8
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login2/terms/LoginTermsFragment2.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.login2.terms
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.args
+import im.vector.app.core.extensions.cleanup
+import im.vector.app.core.extensions.configureWith
+import im.vector.app.core.extensions.toReducedUrl
+import im.vector.app.core.utils.openUrlInChromeCustomTab
+import im.vector.app.databinding.FragmentLoginTerms2Binding
+import im.vector.app.features.login.terms.LocalizedFlowDataLoginTermsChecked
+import im.vector.app.features.login.terms.LoginTermsFragmentArgument
+import im.vector.app.features.login.terms.LoginTermsViewState
+import im.vector.app.features.login.terms.PolicyController
+import im.vector.app.features.login2.AbstractLoginFragment2
+import im.vector.app.features.login2.LoginAction2
+import im.vector.app.features.login2.LoginViewState2
+import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms
+import javax.inject.Inject
+
+/**
+ * LoginTermsFragment displays the list of policies the user has to accept
+ */
+class LoginTermsFragment2 @Inject constructor(
+ private val policyController: PolicyController
+) : AbstractLoginFragment2(),
+ PolicyController.PolicyControllerListener {
+
+ private val params: LoginTermsFragmentArgument by args()
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginTerms2Binding {
+ return FragmentLoginTerms2Binding.inflate(inflater, container, false)
+ }
+
+ private var loginTermsViewState: LoginTermsViewState = LoginTermsViewState(emptyList())
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupViews()
+ views.loginTermsPolicyList.configureWith(policyController)
+ policyController.listener = this
+
+ val list = ArrayList()
+
+ params.localizedFlowDataLoginTerms
+ .forEach {
+ list.add(LocalizedFlowDataLoginTermsChecked(it))
+ }
+
+ loginTermsViewState = LoginTermsViewState(list)
+ }
+
+ private fun setupViews() {
+ views.loginTermsSubmit.setOnClickListener { submit() }
+ }
+
+ override fun onDestroyView() {
+ views.loginTermsPolicyList.cleanup()
+ policyController.listener = null
+ super.onDestroyView()
+ }
+
+ private fun renderState() {
+ policyController.setData(loginTermsViewState.localizedFlowDataLoginTermsChecked)
+
+ // Button is enabled only if all checkboxes are checked
+ views.loginTermsSubmit.isEnabled = loginTermsViewState.allChecked()
+ }
+
+ override fun setChecked(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms, isChecked: Boolean) {
+ if (isChecked) {
+ loginTermsViewState.check(localizedFlowDataLoginTerms)
+ } else {
+ loginTermsViewState.uncheck(localizedFlowDataLoginTerms)
+ }
+
+ renderState()
+ }
+
+ override fun openPolicy(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms) {
+ localizedFlowDataLoginTerms.localizedUrl
+ ?.takeIf { it.isNotBlank() }
+ ?.let {
+ openUrlInChromeCustomTab(requireContext(), null, it)
+ }
+ }
+
+ private fun submit() {
+ loginViewModel.handle(LoginAction2.AcceptTerms)
+ }
+
+ override fun updateWithState(state: LoginViewState2) {
+ policyController.homeServer = state.homeServerUrlFromUser.toReducedUrl()
+ renderState()
+ }
+
+ override fun resetViewModel() {
+ loginViewModel.handle(LoginAction2.ResetSignup)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/media/AttachmentProviderFactory.kt b/vector/src/main/java/im/vector/app/features/media/AttachmentProviderFactory.kt
index b549e01551..f067cd7599 100644
--- a/vector/src/main/java/im/vector/app/features/media/AttachmentProviderFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/media/AttachmentProviderFactory.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
+import kotlinx.coroutines.CoroutineScope
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -30,24 +31,31 @@ class AttachmentProviderFactory @Inject constructor(
private val session: Session
) {
- fun createProvider(attachments: List): RoomEventsAttachmentProvider {
+ fun createProvider(attachments: List,
+ coroutineScope: CoroutineScope
+ ): RoomEventsAttachmentProvider {
return RoomEventsAttachmentProvider(
- attachments,
- imageContentRenderer,
- vectorDateFormatter,
- session.fileService(),
- stringProvider
+ attachments = attachments,
+ imageContentRenderer = imageContentRenderer,
+ dateFormatter = vectorDateFormatter,
+ fileService = session.fileService(),
+ coroutineScope = coroutineScope,
+ stringProvider = stringProvider
)
}
- fun createProvider(attachments: List, room: Room?): DataAttachmentRoomProvider {
+ fun createProvider(attachments: List,
+ room: Room?,
+ coroutineScope: CoroutineScope
+ ): DataAttachmentRoomProvider {
return DataAttachmentRoomProvider(
- attachments,
- room,
- imageContentRenderer,
- vectorDateFormatter,
- session.fileService(),
- stringProvider
+ attachments = attachments,
+ room = room,
+ imageContentRenderer = imageContentRenderer,
+ dateFormatter = vectorDateFormatter,
+ fileService = session.fileService(),
+ coroutineScope = coroutineScope,
+ stringProvider = stringProvider
)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
index 53996171a5..ca469bfbcb 100644
--- a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
@@ -31,8 +31,8 @@ import im.vector.lib.attachmentviewer.AttachmentInfo
import im.vector.lib.attachmentviewer.AttachmentSourceProvider
import im.vector.lib.attachmentviewer.ImageLoaderTarget
import im.vector.lib.attachmentviewer.VideoLoaderTarget
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
@@ -44,6 +44,7 @@ abstract class BaseAttachmentProvider(
private val attachments: List,
private val imageContentRenderer: ImageContentRenderer,
protected val fileService: FileService,
+ private val coroutineScope: CoroutineScope,
private val dateFormatter: VectorDateFormatter,
private val stringProvider: StringProvider
) : AttachmentSourceProvider {
@@ -155,7 +156,7 @@ abstract class BaseAttachmentProvider(
target.onVideoURLReady(info.uid, data.url)
} else {
target.onVideoFileLoading(info.uid)
- GlobalScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.IO) {
val result = runCatching {
fileService.downloadFile(
fileName = data.filename,
@@ -178,5 +179,5 @@ abstract class BaseAttachmentProvider(
// TODO("Not yet implemented")
}
- abstract fun getFileForSharing(position: Int, callback: ((File?) -> Unit))
+ abstract suspend fun getFileForSharing(position: Int): File?
}
diff --git a/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt b/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
index 630433506f..31162f309f 100644
--- a/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
@@ -19,10 +19,8 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.lib.attachmentviewer.AttachmentInfo
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.CoroutineScope
+import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -35,8 +33,16 @@ class DataAttachmentRoomProvider(
imageContentRenderer: ImageContentRenderer,
dateFormatter: VectorDateFormatter,
fileService: FileService,
+ coroutineScope: CoroutineScope,
stringProvider: StringProvider
-) : BaseAttachmentProvider(attachments, imageContentRenderer, fileService, dateFormatter, stringProvider) {
+) : BaseAttachmentProvider(
+ attachments = attachments,
+ imageContentRenderer = imageContentRenderer,
+ fileService = fileService,
+ coroutineScope = coroutineScope,
+ dateFormatter = dateFormatter,
+ stringProvider = stringProvider
+) {
override fun getAttachmentInfoAt(position: Int): AttachmentInfo {
return getItem(position).let {
@@ -78,20 +84,17 @@ class DataAttachmentRoomProvider(
return room?.getTimeLineEvent(item.eventId)
}
- override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
- val item = getItem(position)
- GlobalScope.launch {
- val result = runCatching {
- fileService.downloadFile(
- fileName = item.filename,
- mimeType = item.mimeType,
- url = item.url,
- elementToDecrypt = item.elementToDecrypt
- )
- }
- withContext(Dispatchers.Main) {
- callback(result.getOrNull())
- }
- }
+ override suspend fun getFileForSharing(position: Int): File? {
+ return getItem(position)
+ .let { item ->
+ tryOrNull {
+ fileService.downloadFile(
+ fileName = item.filename,
+ mimeType = item.mimeType,
+ url = item.url,
+ elementToDecrypt = item.elementToDecrypt
+ )
+ }
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
index fc6d5a1f22..1e0a3a2ad9 100644
--- a/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
@@ -19,10 +19,8 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.lib.attachmentviewer.AttachmentInfo
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.CoroutineScope
+import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
@@ -41,8 +39,16 @@ class RoomEventsAttachmentProvider(
imageContentRenderer: ImageContentRenderer,
dateFormatter: VectorDateFormatter,
fileService: FileService,
+ coroutineScope: CoroutineScope,
stringProvider: StringProvider
-) : BaseAttachmentProvider(attachments, imageContentRenderer, fileService, dateFormatter, stringProvider) {
+) : BaseAttachmentProvider(
+ attachments = attachments,
+ imageContentRenderer = imageContentRenderer,
+ fileService = fileService,
+ coroutineScope = coroutineScope,
+ dateFormatter = dateFormatter,
+ stringProvider = stringProvider
+) {
override fun getAttachmentInfoAt(position: Int): AttachmentInfo {
return getItem(position).let {
@@ -121,24 +127,19 @@ class RoomEventsAttachmentProvider(
return getItem(position)
}
- override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
- getItem(position).let { timelineEvent ->
-
- val messageContent = timelineEvent.root.getClearContent().toModel()
- as? MessageWithAttachmentContent
- ?: return@let
- GlobalScope.launch {
- val result = runCatching {
- fileService.downloadFile(
- fileName = messageContent.body,
- mimeType = messageContent.mimeType,
- url = messageContent.getFileUrl(),
- elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
+ override suspend fun getFileForSharing(position: Int): File? {
+ return getItem(position)
+ .let { timelineEvent ->
+ timelineEvent.root.getClearContent().toModel() as? MessageWithAttachmentContent
}
- withContext(Dispatchers.Main) {
- callback(result.getOrNull())
+ ?.let { messageContent ->
+ tryOrNull {
+ fileService.downloadFile(
+ fileName = messageContent.body,
+ mimeType = messageContent.mimeType,
+ url = messageContent.getFileUrl(),
+ elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
+ }
}
- }
- }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
index c632a008ce..bc3acf3eec 100644
--- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
@@ -28,7 +28,7 @@ import androidx.core.transition.addListener
import androidx.core.view.ViewCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
-import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import androidx.transition.Transition
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
@@ -42,6 +42,9 @@ import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils
import im.vector.lib.attachmentviewer.AttachmentCommands
import im.vector.lib.attachmentviewer.AttachmentViewerActivity
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import timber.log.Timber
import javax.inject.Inject
@@ -119,11 +122,11 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen
val inMemoryData = intent.getParcelableArrayListExtra(EXTRA_IN_MEMORY_DATA)
val sourceProvider = if (inMemoryData != null) {
initialIndex = inMemoryData.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0)
- dataSourceFactory.createProvider(inMemoryData, room)
+ dataSourceFactory.createProvider(inMemoryData, room, lifecycleScope)
} else {
val events = room?.getAttachmentMessages().orEmpty()
initialIndex = events.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0)
- dataSourceFactory.createProvider(events)
+ dataSourceFactory.createProvider(events, lifecycleScope)
}
sourceProvider.interactionListener = this
setSourceProvider(sourceProvider)
@@ -264,9 +267,15 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen
}
override fun onShareTapped() {
- currentSourceProvider?.getFileForSharing(currentPosition) { data ->
- if (data != null && lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
- shareMedia(this@VectorAttachmentViewerActivity, data, getMimeTypeFromUri(this@VectorAttachmentViewerActivity, data.toUri()))
+ lifecycleScope.launch(Dispatchers.IO) {
+ val file = currentSourceProvider?.getFileForSharing(currentPosition) ?: return@launch
+
+ withContext(Dispatchers.Main) {
+ shareMedia(
+ this@VectorAttachmentViewerActivity,
+ file,
+ getMimeTypeFromUri(this@VectorAttachmentViewerActivity, file.toUri())
+ )
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
index 80d2d8ba45..635de2ba16 100644
--- a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
@@ -25,8 +25,9 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.files.LocalFilesHelper
+import im.vector.app.features.session.coroutineScope
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
@@ -39,6 +40,9 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
private val activeSessionHolder: ActiveSessionHolder,
private val errorFormatter: ErrorFormatter) {
+ private val sessionScope: CoroutineScope
+ get() = activeSessionHolder.getActiveSession().coroutineScope
+
@Parcelize
data class Data(
override val eventId: String,
@@ -76,7 +80,7 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
thumbnailView.isVisible = true
loadingView.isVisible = true
- GlobalScope.launch {
+ sessionScope.launch {
val result = runCatching {
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
@@ -119,7 +123,7 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
thumbnailView.isVisible = true
loadingView.isVisible = true
- GlobalScope.launch {
+ sessionScope.launch {
val result = runCatching {
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
index bf8fa497ff..3abf01583c 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
@@ -54,6 +54,9 @@ import im.vector.app.features.home.room.detail.search.SearchActivity
import im.vector.app.features.home.room.detail.search.SearchArgs
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
import im.vector.app.features.invite.InviteUsersToRoomActivity
+import im.vector.app.features.login.LoginActivity
+import im.vector.app.features.login.LoginConfig
+import im.vector.app.features.login2.LoginActivity2
import im.vector.app.features.matrixto.MatrixToBottomSheet
import im.vector.app.features.media.AttachmentData
import im.vector.app.features.media.BigImageViewerActivity
@@ -99,6 +102,16 @@ class DefaultNavigator @Inject constructor(
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
) : Navigator {
+ override fun openLogin(context: Context, loginConfig: LoginConfig?, flags: Int) {
+ val intent = if (context.resources.getBoolean(R.bool.useLoginV2)) {
+ LoginActivity2.newIntent(context, loginConfig)
+ } else {
+ LoginActivity.newIntent(context, loginConfig)
+ }
+ intent.addFlags(flags)
+ context.startActivity(intent)
+ }
+
override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) {
if (sessionHolder.getSafeActiveSession()?.getRoom(roomId) == null) {
fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast())
diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
index 2302a749e7..444c48bddb 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
@@ -23,6 +23,7 @@ import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.core.util.Pair
import im.vector.app.features.crypto.recover.SetupMode
+import im.vector.app.features.login.LoginConfig
import im.vector.app.features.media.AttachmentData
import im.vector.app.features.pin.PinMode
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData
@@ -36,6 +37,8 @@ import org.matrix.android.sdk.api.util.MatrixItem
interface Navigator {
+ fun openLogin(context: Context, loginConfig: LoginConfig? = null, flags: Int = 0)
+
fun openRoom(context: Context, roomId: String, eventId: String? = null, buildTask: Boolean = false)
sealed class PostSwitchSpaceAction {
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
index 5cdc915c81..1b72793f89 100755
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
@@ -562,9 +562,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
NotificationCompat.Action.Builder(R.drawable.vector_notification_quick_reply,
stringProvider.getString(R.string.action_quick_reply), replyPendingIntent)
.addRemoteInput(remoteInput)
- .build()?.let {
- addAction(it)
- }
+ .build()
+ .let { addAction(it) }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt
index 02c9c7f717..ee4e0e05b5 100644
--- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt
@@ -26,7 +26,6 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.FragmentProgressBinding
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.LoadingFragment
-import im.vector.app.features.login.LoginActivity
import javax.inject.Inject
class PermalinkHandlerActivity : VectorBaseActivity() {
@@ -71,9 +70,10 @@ class PermalinkHandlerActivity : VectorBaseActivity() {
}
private fun startLoginActivity() {
- val intent = LoginActivity.newIntent(this, null)
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
- startActivity(intent)
+ navigator.openLogin(
+ context = this,
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
+ )
finish()
}
}
diff --git a/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt b/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt
index adc618d82e..9c55b88805 100644
--- a/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt
+++ b/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt
@@ -60,6 +60,7 @@ class PinLocker @Inject constructor(
return liveState
}
+ @Suppress("EXPERIMENTAL_API_USAGE")
private fun computeState() {
GlobalScope.launch {
val state = if (shouldBeLocked && pinCodeStore.hasEncodedPin()) {
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt b/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt
index 304720dfb0..2f8e013f46 100644
--- a/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt
@@ -88,6 +88,7 @@ class VectorFileLogger @Inject constructor(
}
}
+ @Suppress("EXPERIMENTAL_API_USAGE")
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
fileHandler ?: return
GlobalScope.launch(Dispatchers.IO) {
diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt
index 095e250602..51dc62af8b 100644
--- a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt
@@ -19,6 +19,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.lifecycle.lifecycleScope
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.EmojiChooserFragmentBinding
@@ -51,6 +52,8 @@ class EmojiChooserFragment @Inject constructor(
}
}
+ override fun getCoroutineScope() = lifecycleScope
+
override fun firstVisibleSectionChange(section: Int) {
viewModel.setCurrentSection(section)
}
diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiRecyclerAdapter.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiRecyclerAdapter.kt
index 92bc21be25..45d26e81eb 100644
--- a/vector/src/main/java/im/vector/app/features/reactions/EmojiRecyclerAdapter.kt
+++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiRecyclerAdapter.kt
@@ -31,8 +31,8 @@ import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import im.vector.app.R
import im.vector.app.features.reactions.data.EmojiDataSource
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.abs
@@ -221,7 +221,7 @@ class EmojiRecyclerAdapter @Inject constructor(
}
override fun getItemCount() = dataSource.rawData.categories
- .sumBy { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size }
+ .sumOf { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size }
abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(s: String?)
@@ -278,6 +278,7 @@ class EmojiRecyclerAdapter @Inject constructor(
}
interface InteractionListener {
+ fun getCoroutineScope(): CoroutineScope
fun firstVisibleSectionChange(section: Int)
}
@@ -323,11 +324,11 @@ class EmojiRecyclerAdapter @Inject constructor(
// Log.i("SCROLL SPEED","scroll speed $dy")
isFastScroll = abs(dy) > 50
val visible = (recyclerView.layoutManager as GridLayoutManager).findFirstCompletelyVisibleItemPosition()
- GlobalScope.launch {
+ interactionListener?.getCoroutineScope()?.launch {
val section = getSectionForAbsoluteIndex(visible)
if (section != currentFirstVisibleSection) {
currentFirstVisibleSection = section
- GlobalScope.launch(Dispatchers.Main) {
+ interactionListener?.getCoroutineScope()?.launch(Dispatchers.Main) {
interactionListener?.firstVisibleSectionChange(currentFirstVisibleSection)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt
index 8c510f568a..ac015ad1f0 100644
--- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt
+++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt
@@ -45,19 +45,20 @@ class EmojiSearchResultController @Inject constructor(
override fun buildModels(data: EmojiSearchResultViewState?) {
val results = data?.results ?: return
+ val host = this
if (results.isEmpty()) {
if (data.query.isEmpty()) {
// display 'Type something to find'
genericFooterItem {
id("type.query.item")
- text(stringProvider.getString(R.string.reaction_search_type_hint))
+ text(host.stringProvider.getString(R.string.reaction_search_type_hint))
}
} else {
// Display no search Results
genericFooterItem {
id("no.results.item")
- text(stringProvider.getString(R.string.no_result_placeholder))
+ text(host.stringProvider.getString(R.string.no_result_placeholder))
}
}
} else {
@@ -66,9 +67,9 @@ class EmojiSearchResultController @Inject constructor(
emojiSearchResultItem {
id(it.name)
emojiItem(it)
- emojiTypeFace(emojiTypeface)
+ emojiTypeFace(host.emojiTypeface)
currentQuery(data.query)
- onClickListener(listener)
+ onClickListener(host.listener)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt
index b71b494948..32c87bffd4 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt
@@ -42,6 +42,7 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
var callback: Callback? = null
override fun buildModels(viewState: PublicRoomsViewState) {
+ val host = this
val publicRooms = viewState.publicRooms
val unknownRoomItem = viewState.buildUnknownRoomIfNeeded()
@@ -51,7 +52,7 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
// No result
noResultItem {
id("noResult")
- text(stringProvider.getString(R.string.no_result_placeholder))
+ text(host.stringProvider.getString(R.string.no_result_placeholder))
}
} else {
publicRooms.forEach {
@@ -71,7 +72,7 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
}
onVisibilityStateChanged { _, _, visibilityState ->
if (visibilityState == VisibilityState.VISIBLE) {
- callback?.loadMore()
+ host.callback?.loadMore()
}
}
}
@@ -81,15 +82,16 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
if (viewState.asyncPublicRoomsRequest is Fail) {
errorWithRetryItem {
id("error")
- text(errorFormatter.toHumanReadable(viewState.asyncPublicRoomsRequest.error))
- listener { callback?.loadMore() }
+ text(host.errorFormatter.toHumanReadable(viewState.asyncPublicRoomsRequest.error))
+ listener { host.callback?.loadMore() }
}
}
}
private fun buildPublicRoom(publicRoom: PublicRoom, viewState: PublicRoomsViewState) {
+ val host = this
publicRoomItem {
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
id(publicRoom.roomId)
matrixItem(publicRoom.toMatrixItem())
roomAlias(publicRoom.getPrimaryAlias())
@@ -107,10 +109,10 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
joinState(joinState)
joinListener {
- callback?.onPublicRoomJoin(publicRoom)
+ host.callback?.onPublicRoomJoin(publicRoom)
}
globalListener {
- callback?.onPublicRoomClicked(publicRoom, joinState)
+ host.callback?.onPublicRoomClicked(publicRoom, joinState)
}
}
}
@@ -124,13 +126,14 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
isRoomId -> MatrixItem.RoomItem(roomIdOrAlias)
else -> null
}
+ val host = this@PublicRoomsController
return roomItem?.let {
UnknownRoomItem_().apply {
id(roomIdOrAlias)
matrixItem(it)
- avatarRenderer(this@PublicRoomsController.avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
globalListener {
- callback?.onUnknownRoomClicked(roomIdOrAlias)
+ host.callback?.onUnknownRoomClicked(roomIdOrAlias)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt
index fba3d4514c..03c98d249c 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt
@@ -49,12 +49,13 @@ class CreateRoomController @Inject constructor(
private fun buildForm(viewState: CreateRoomViewState, enableFormElement: Boolean) {
val enableNonSimplifiedMode = !vectorPreferences.simplifiedMode()
+ val host = this
formEditableAvatarItem {
id("avatar")
enabled(enableFormElement)
imageUri(viewState.avatarUri)
- clickListener { listener?.onAvatarChange() }
- deleteListener { listener?.onAvatarDelete() }
+ clickListener { host.listener?.onAvatarChange() }
+ deleteListener { host.listener?.onAvatarDelete() }
}
settingsSectionTitleItem {
id("nameSection")
@@ -64,10 +65,10 @@ class CreateRoomController @Inject constructor(
id("name")
enabled(enableFormElement)
value(viewState.roomName)
- hint(stringProvider.getString(R.string.create_room_name_hint))
+ hint(host.stringProvider.getString(R.string.create_room_name_hint))
onTextChange { text ->
- listener?.onNameChange(text)
+ host.listener?.onNameChange(text)
}
}
settingsSectionTitleItem {
@@ -78,10 +79,10 @@ class CreateRoomController @Inject constructor(
id("topic")
enabled(enableFormElement)
value(viewState.roomTopic)
- hint(stringProvider.getString(R.string.create_room_topic_hint))
+ hint(host.stringProvider.getString(R.string.create_room_topic_hint))
onTextChange { text ->
- listener?.onTopicChange(text)
+ host.listener?.onTopicChange(text)
}
}
// Following settings are for advanced users only
@@ -93,13 +94,13 @@ class CreateRoomController @Inject constructor(
formSwitchItem {
id("public")
enabled(enableFormElement)
- title(stringProvider.getString(R.string.create_room_public_title))
- summary(stringProvider.getString(R.string.create_room_public_description))
+ title(host.stringProvider.getString(R.string.create_room_public_title))
+ summary(host.stringProvider.getString(R.string.create_room_public_description))
switchChecked(viewState.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public)
showDivider(viewState.roomVisibilityType !is CreateRoomViewState.RoomVisibilityType.Public)
listener { value ->
- listener?.setIsPublic(value)
+ host.listener?.setIsPublic(value)
}
}
if (viewState.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public) {
@@ -110,11 +111,11 @@ class CreateRoomController @Inject constructor(
value(viewState.roomVisibilityType.aliasLocalPart)
homeServer(":" + viewState.homeServerName)
errorMessage(
- roomAliasErrorFormatter.format(
+ host.roomAliasErrorFormatter.format(
(((viewState.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError)
)
onTextChange { value ->
- listener?.setAliasLocalPart(value)
+ host.listener?.setAliasLocalPart(value)
}
}
} else {
@@ -122,36 +123,36 @@ class CreateRoomController @Inject constructor(
formSwitchItem {
id("encryption")
enabled(enableFormElement)
- title(stringProvider.getString(R.string.create_room_encryption_title))
+ title(host.stringProvider.getString(R.string.create_room_encryption_title))
summary(
if (viewState.hsAdminHasDisabledE2E) {
- stringProvider.getString(R.string.settings_hs_admin_e2e_disabled)
+ host.stringProvider.getString(R.string.settings_hs_admin_e2e_disabled)
} else {
- stringProvider.getString(R.string.create_room_encryption_description)
+ host.stringProvider.getString(R.string.create_room_encryption_description)
}
)
switchChecked(viewState.isEncrypted)
listener { value ->
- listener?.setIsEncrypted(value)
+ host.listener?.setIsEncrypted(value)
}
}
}
formAdvancedToggleItem {
id("showAdvanced")
- title(stringProvider.getString(if (viewState.showAdvanced) R.string.hide_advanced else R.string.show_advanced))
+ title(host.stringProvider.getString(if (viewState.showAdvanced) R.string.hide_advanced else R.string.show_advanced))
expanded(!viewState.showAdvanced)
- listener { listener?.toggleShowAdvanced() }
+ listener { host.listener?.toggleShowAdvanced() }
}
if (viewState.showAdvanced) {
formSwitchItem {
id("federation")
enabled(enableFormElement)
- title(stringProvider.getString(R.string.create_room_disable_federation_title, viewState.homeServerName))
- summary(stringProvider.getString(R.string.create_room_disable_federation_description))
+ title(host.stringProvider.getString(R.string.create_room_disable_federation_title, viewState.homeServerName))
+ summary(host.stringProvider.getString(R.string.create_room_disable_federation_description))
switchChecked(viewState.disableFederation)
showDivider(false)
- listener { value -> listener?.setDisableFederation(value) }
+ listener { value -> host.listener?.setDisableFederation(value) }
}
}
}
@@ -159,7 +160,7 @@ class CreateRoomController @Inject constructor(
id("submit")
enabled(enableFormElement)
buttonTitleId(R.string.create_room_action_create)
- buttonClickListener { listener?.submit() }
+ buttonClickListener { host.listener?.submit() }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt
index 2dd3b509a8..75e9807bd0 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt
@@ -38,6 +38,7 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid
var index = 0
override fun buildModels(viewState: RoomDirectoryPickerViewState) {
+ val host = this
val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest
when (asyncThirdPartyProtocol) {
@@ -56,24 +57,25 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid
is Fail -> {
errorWithRetryItem {
id("error")
- text(errorFormatter.toHumanReadable(asyncThirdPartyProtocol.error))
- listener { callback?.retry() }
+ text(host.errorFormatter.toHumanReadable(asyncThirdPartyProtocol.error))
+ listener { host.callback?.retry() }
}
}
}
}
private fun buildDirectory(roomDirectoryData: RoomDirectoryData) {
+ val host = this
roomDirectoryItem {
- id(index++)
+ id(host.index++)
directoryName(roomDirectoryData.displayName)
val description = when {
roomDirectoryData.includeAllNetworks ->
- stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName)
+ host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName)
"Matrix" == roomDirectoryData.displayName ->
- stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName)
+ host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName)
else ->
null
}
@@ -83,7 +85,7 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid
includeAllNetworks(roomDirectoryData.includeAllNetworks)
globalListener {
- callback?.onRoomDirectoryClicked(roomDirectoryData)
+ host.callback?.onRoomDirectoryClicked(roomDirectoryData)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt
index a692eebe40..88a0518cf9 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt
@@ -99,6 +99,7 @@ class RoomMemberProfileController @Inject constructor(
private fun buildSecuritySection(state: RoomMemberProfileViewState) {
// Security
buildProfileSection(stringProvider.getString(R.string.room_profile_section_security))
+ val host = this
if (state.isRoomEncrypted) {
if (state.userMXCrossSigningInfo != null) {
@@ -152,7 +153,7 @@ class RoomMemberProfileController @Inject constructor(
genericFooterItem {
id("verify_footer")
- text(stringProvider.getString(R.string.room_profile_encrypted_subtitle))
+ text(host.stringProvider.getString(R.string.room_profile_encrypted_subtitle))
centered(false)
}
}
@@ -170,7 +171,7 @@ class RoomMemberProfileController @Inject constructor(
} else {
genericFooterItem {
id("verify_footer_not_encrypted")
- text(stringProvider.getString(R.string.room_profile_not_encrypted_subtitle))
+ text(host.stringProvider.getString(R.string.room_profile_not_encrypted_subtitle))
centered(false)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt
index e0730d55f1..48eaa7ba6f 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt
@@ -50,16 +50,15 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
var interactionListener: InteractionListener? = null
override fun buildModels(data: DeviceListViewState?) {
- if (data == null) {
- return
- }
+ data ?: return
+ val host = this
when (data.cryptoDevices) {
Uninitialized -> {
}
is Loading -> {
loadingItem {
id("loading")
- loadingText(stringProvider.getString(R.string.loading))
+ loadingText(host.stringProvider.getString(R.string.loading))
}
}
is Success -> {
@@ -77,11 +76,11 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
style(ItemStyle.BIG_TEXT)
titleIconResourceId(if (allGreen) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning)
title(
- stringProvider.getString(
+ host.stringProvider.getString(
if (allGreen) R.string.verification_profile_verified else R.string.verification_profile_warning
)
)
- description(stringProvider.getString(R.string.verification_conclusion_ok_notice))
+ description(host.stringProvider.getString(R.string.verification_conclusion_ok_notice))
}
if (vectorPreferences.developerMode()) {
@@ -92,13 +91,13 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
genericItem {
id("sessions")
style(ItemStyle.BIG_TEXT)
- title(stringProvider.getString(R.string.room_member_profile_sessions_section_title))
+ title(host.stringProvider.getString(R.string.room_member_profile_sessions_section_title))
}
if (deviceList.isEmpty()) {
// Can this really happen?
genericFooterItem {
id("empty")
- text(stringProvider.getString(R.string.search_no_results))
+ text(host.stringProvider.getString(R.string.search_no_results))
}
} else {
// Build list of device with status
@@ -107,14 +106,14 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
id(device.deviceId)
titleIconResourceId(if (device.isVerified) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning)
apply {
- if (vectorPreferences.developerMode()) {
+ if (host.vectorPreferences.developerMode()) {
val seq = span {
+(device.displayName() ?: device.deviceId)
+"\n"
span {
text = "(${device.deviceId})"
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(14)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(14)
}
}
title(seq)
@@ -123,17 +122,17 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
}
}
value(
- stringProvider.getString(
+ host.stringProvider.getString(
if (device.isVerified) R.string.trusted else R.string.not_trusted
)
)
valueColorInt(
- colorProvider.getColor(
+ host.colorProvider.getColor(
if (device.isVerified) R.color.riotx_positive_accent else R.color.riotx_destructive_accent
)
)
itemClickAction(View.OnClickListener {
- interactionListener?.onDeviceSelected(device)
+ host.interactionListener?.onDeviceSelected(device)
})
}
}
@@ -142,7 +141,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
is Fail -> {
errorWithRetryItem {
id("error")
- text(stringProvider.getString(R.string.room_member_profile_failed_to_get_devices))
+ text(host.stringProvider.getString(R.string.room_member_profile_failed_to_get_devices))
listener {
// TODO
}
@@ -152,6 +151,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
}
private fun addDebugInfo(data: DeviceListViewState) {
+ val host = this
data.memberCrossSigningKey?.masterKey()?.let {
genericItemWithValue {
id("msk")
@@ -161,8 +161,8 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
+"Master Key:\n"
span {
text = it.unpaddedBase64PublicKey ?: ""
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(12)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(12)
}
}
)
@@ -177,8 +177,8 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
+"User Key:\n"
span {
text = it.unpaddedBase64PublicKey ?: ""
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(12)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(12)
}
}
)
@@ -193,8 +193,8 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
+"Self Signed Key:\n"
span {
text = it.unpaddedBase64PublicKey ?: ""
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(12)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(12)
}
}
)
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt
index 4c28acd904..f12f17ae14 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt
@@ -44,6 +44,7 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
var interactionListener: InteractionListener? = null
override fun buildModels(data: DeviceListViewState?) {
+ val host = this
data?.selectedDevice?.let {
val isVerified = it.trustLevel?.isVerified() == true
genericItem {
@@ -51,7 +52,7 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
style(ItemStyle.BIG_TEXT)
titleIconResourceId(if (isVerified) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning)
title(
- stringProvider.getString(
+ host.stringProvider.getString(
if (isVerified) R.string.verification_profile_verified else R.string.verification_profile_warning
)
)
@@ -59,16 +60,16 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
genericFooterItem {
id("desc")
centered(false)
- textColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
+ textColor(host.colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
apply {
if (isVerified) {
// TODO FORMAT
- text(stringProvider.getString(R.string.verification_profile_device_verified_because,
+ text(host.stringProvider.getString(R.string.verification_profile_device_verified_because,
data.userItem?.displayName ?: "",
data.userItem?.id ?: ""))
} else {
// TODO what if mine
- text(stringProvider.getString(R.string.verification_profile_device_new_signing,
+ text(host.stringProvider.getString(R.string.verification_profile_device_new_signing,
data.userItem?.displayName ?: "",
data.userItem?.id ?: ""))
}
@@ -84,8 +85,8 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
+(it.displayName() ?: "")
span {
text = " (${it.deviceId})"
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(14)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(14)
}
}
)
@@ -95,18 +96,18 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
genericFooterItem {
id("warn")
centered(false)
- textColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
- text(stringProvider.getString(R.string.verification_profile_device_untrust_info))
+ textColor(host.colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
+ text(host.stringProvider.getString(R.string.verification_profile_device_untrust_info))
}
bottomSheetVerificationActionItem {
id("verify")
- title(stringProvider.getString(R.string.cross_signing_verify_by_emoji))
- titleColor(colorProvider.getColor(R.color.riotx_accent))
+ title(host.stringProvider.getString(R.string.cross_signing_verify_by_emoji))
+ titleColor(host.colorProvider.getColor(R.color.riotx_accent))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColor(R.color.riotx_accent))
+ iconColor(host.colorProvider.getColor(R.color.riotx_accent))
listener {
- interactionListener?.onVerifyManually(it)
+ host.interactionListener?.onVerifyManually(it)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
index 084d4ea297..2cfb9fb9d1 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
@@ -62,9 +62,8 @@ class RoomProfileController @Inject constructor(
}
override fun buildModels(data: RoomProfileViewState?) {
- if (data == null) {
- return
- }
+ data ?: return
+ val host = this
val roomSummary = data.roomSummary() ?: return
val enableNonSimplifiedMode = !vectorPreferences.simplifiedMode()
@@ -85,7 +84,7 @@ class RoomProfileController @Inject constructor(
}
override fun onUrlLongClicked(url: String): Boolean {
- callback?.onUrlInTopicLongClicked(url)
+ host.callback?.onUrlInTopicLongClicked(url)
return true
}
}))
@@ -102,7 +101,7 @@ class RoomProfileController @Inject constructor(
genericFooterItem {
id("e2e info")
centered(false)
- text(stringProvider.getString(learnMoreSubtitle))
+ text(host.stringProvider.getString(learnMoreSubtitle))
}
// SC: Move down in the list, this one-time action is not important to enough to show this prevalent at the top
//buildEncryptionAction(data.actionPermissions, roomSummary)
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt
index 0b695031c5..6edeeedca3 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt
@@ -77,34 +77,36 @@ class RoomAliasController @Inject constructor(
}
private fun buildRoomDirectoryVisibility(data: RoomAliasViewState) {
+ val host = this
when (data.roomDirectoryVisibility) {
Uninitialized -> Unit
is Loading -> Unit
is Success -> {
formSwitchItem {
id("roomVisibility")
- title(stringProvider.getString(R.string.room_alias_publish_to_directory, data.homeServerName))
+ title(host.stringProvider.getString(R.string.room_alias_publish_to_directory, data.homeServerName))
showDivider(false)
switchChecked(data.roomDirectoryVisibility() == RoomDirectoryVisibility.PUBLIC)
listener {
if (it) {
- callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PUBLIC)
+ host.callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PUBLIC)
} else {
- callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PRIVATE)
+ host.callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PRIVATE)
}
}
}
}
is Fail -> {
errorWithRetryItem {
- text(stringProvider.getString(R.string.room_alias_publish_to_directory_error,
- errorFormatter.toHumanReadable(data.roomDirectoryVisibility.error)))
+ text(host.stringProvider.getString(R.string.room_alias_publish_to_directory_error,
+ host.errorFormatter.toHumanReadable(data.roomDirectoryVisibility.error)))
}
}
}
}
private fun buildPublishInfo(data: RoomAliasViewState) {
+ val host = this
buildProfileSection(
stringProvider.getString(R.string.room_alias_published_alias_title)
)
@@ -120,8 +122,8 @@ class RoomAliasController @Inject constructor(
profileActionItem {
id("canonical")
title(data.canonicalAlias)
- subtitle(stringProvider.getString(R.string.room_alias_published_alias_main))
- listener { callback?.openAliasDetail(canonicalAlias) }
+ subtitle(host.stringProvider.getString(R.string.room_alias_published_alias_main))
+ listener { host.callback?.openAliasDetail(canonicalAlias) }
}
}
@@ -143,7 +145,7 @@ class RoomAliasController @Inject constructor(
profileActionItem {
id("alt_$idx")
title(altAlias)
- listener { callback?.openAliasDetail(altAlias) }
+ listener { host.callback?.openAliasDetail(altAlias) }
}
}
}
@@ -154,14 +156,15 @@ class RoomAliasController @Inject constructor(
}
private fun buildPublishManuallyForm(data: RoomAliasViewState) {
+ val host = this
when (data.publishManuallyState) {
RoomAliasViewState.AddAliasState.Hidden -> Unit
RoomAliasViewState.AddAliasState.Closed -> {
settingsButtonItem {
id("publishManually")
- colorProvider(colorProvider)
+ colorProvider(host.colorProvider)
buttonTitleId(R.string.room_alias_published_alias_add_manually)
- buttonClickListener { callback?.toggleManualPublishForm() }
+ buttonClickListener { host.callback?.toggleManualPublishForm() }
}
}
is RoomAliasViewState.AddAliasState.Editing -> {
@@ -169,29 +172,30 @@ class RoomAliasController @Inject constructor(
id("publishManuallyEdit")
value(data.publishManuallyState.value)
showBottomSeparator(false)
- hint(stringProvider.getString(R.string.room_alias_address_hint))
+ hint(host.stringProvider.getString(R.string.room_alias_address_hint))
inputType(InputType.TYPE_CLASS_TEXT)
onTextChange { text ->
- callback?.setNewAlias(text)
+ host.callback?.setNewAlias(text)
}
}
settingsContinueCancelItem {
id("publishManuallySubmit")
- continueText(stringProvider.getString(R.string.room_alias_published_alias_add_manually_submit))
- continueOnClick { callback?.addAlias() }
- cancelOnClick { callback?.toggleManualPublishForm() }
+ continueText(host.stringProvider.getString(R.string.room_alias_published_alias_add_manually_submit))
+ continueOnClick { host.callback?.addAlias() }
+ cancelOnClick { host.callback?.toggleManualPublishForm() }
}
}
}
}
private fun buildLocalInfo(data: RoomAliasViewState) {
+ val host = this
buildProfileSection(
stringProvider.getString(R.string.room_alias_local_address_title)
)
settingsInfoItem {
id("localInfo")
- helperText(stringProvider.getString(R.string.room_alias_local_address_subtitle, data.homeServerName))
+ helperText(host.stringProvider.getString(R.string.room_alias_local_address_subtitle, data.homeServerName))
}
when (val localAliases = data.localAliases) {
@@ -211,7 +215,7 @@ class RoomAliasController @Inject constructor(
profileActionItem {
id("loc_$idx")
title(localAlias)
- listener { callback?.openAliasDetail(localAlias) }
+ listener { host.callback?.openAliasDetail(localAlias) }
}
}
}
@@ -219,7 +223,7 @@ class RoomAliasController @Inject constructor(
is Fail -> {
errorWithRetryItem {
id("alt_error")
- text(errorFormatter.toHumanReadable(localAliases.error))
+ text(host.errorFormatter.toHumanReadable(localAliases.error))
}
}
}
@@ -229,14 +233,15 @@ class RoomAliasController @Inject constructor(
}
private fun buildAddLocalAlias(data: RoomAliasViewState) {
+ val host = this
when (data.newLocalAliasState) {
RoomAliasViewState.AddAliasState.Hidden -> Unit
RoomAliasViewState.AddAliasState.Closed -> {
settingsButtonItem {
id("newLocalAliasButton")
- colorProvider(colorProvider)
+ colorProvider(host.colorProvider)
buttonTitleId(R.string.room_alias_local_address_add)
- buttonClickListener { callback?.toggleLocalAliasForm() }
+ buttonClickListener { host.callback?.toggleLocalAliasForm() }
}
}
is RoomAliasViewState.AddAliasState.Editing -> {
@@ -245,16 +250,16 @@ class RoomAliasController @Inject constructor(
value(data.newLocalAliasState.value)
homeServer(":" + data.homeServerName)
showBottomSeparator(false)
- errorMessage(roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError))
+ errorMessage(host.roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError))
onTextChange { value ->
- callback?.setNewLocalAliasLocalPart(value)
+ host.callback?.setNewLocalAliasLocalPart(value)
}
}
settingsContinueCancelItem {
id("newLocalAliasSubmit")
- continueText(stringProvider.getString(R.string.action_add))
- continueOnClick { callback?.addLocalAlias() }
- cancelOnClick { callback?.toggleLocalAliasForm() }
+ continueText(host.stringProvider.getString(R.string.action_add))
+ continueOnClick { host.callback?.addLocalAlias() }
+ cancelOnClick { host.callback?.toggleLocalAliasForm() }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt
index 157037c13d..b030afd503 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt
@@ -73,12 +73,13 @@ class RoomAliasBottomSheetController @Inject constructor() : TypedEpoxyControlle
}
private fun RoomAliasBottomSheetSharedAction.toBottomSheetItem(index: Int) {
+ val host = this@RoomAliasBottomSheetController
return bottomSheetActionItem {
id("action_$index")
iconRes(iconResId)
textRes(titleRes)
destructive(this@toBottomSheetItem.destructive)
- listener(View.OnClickListener { listener?.didSelectMenuAction(this@toBottomSheetItem) })
+ listener(View.OnClickListener { host.listener?.didSelectMenuAction(this@toBottomSheetItem) })
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt
index 2a0c787a7a..6c2fac98d8 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt
@@ -52,6 +52,7 @@ class RoomBannedMemberListController @Inject constructor(
override fun buildModels(data: RoomBannedMemberListViewState?) {
val bannedList = data?.bannedMemberSummaries?.invoke() ?: return
+ val host = this
val quantityString = stringProvider.getQuantityString(R.plurals.room_settings_banned_users_count, bannedList.size, bannedList.size)
@@ -74,7 +75,7 @@ class RoomBannedMemberListController @Inject constructor(
profileMatrixItemWithProgress {
id(roomMember.userId)
matrixItem(roomMember.toMatrixItem())
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
apply {
if (actionInProgress) {
inProgress(true)
@@ -83,7 +84,7 @@ class RoomBannedMemberListController @Inject constructor(
inProgress(false)
editable(true)
clickListener { _ ->
- callback?.onUnbanClicked(roomMember)
+ host.callback?.onUnbanClicked(roomMember)
}
}
}
@@ -92,7 +93,7 @@ class RoomBannedMemberListController @Inject constructor(
between = { _, roomMemberBefore ->
dividerItem {
id("divider_${roomMemberBefore.userId}")
- color(dividerColor)
+ color(host.dividerColor)
}
}
)
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt
index eda461de14..5f070818a0 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt
@@ -55,6 +55,7 @@ class RoomMemberListController @Inject constructor(
override fun buildModels(data: RoomMemberListViewState?) {
data ?: return
+ val host = this
roomMemberSummaryFilter.filter = data.filter
@@ -93,17 +94,17 @@ class RoomMemberListController @Inject constructor(
profileMatrixItem {
id(roomMember.userId)
matrixItem(roomMember.toMatrixItem())
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
clickListener { _ ->
- callback?.onRoomMemberClicked(roomMember)
+ host.callback?.onRoomMemberClicked(roomMember)
}
}
},
between = { _, roomMemberBefore ->
dividerItem {
id("divider_${roomMemberBefore.userId}")
- color(dividerColor)
+ color(host.dividerColor)
}
}
)
@@ -111,7 +112,7 @@ class RoomMemberListController @Inject constructor(
// Display the threepid invite after the regular invite
dividerItem {
id("divider_threepidinvites")
- color(dividerColor)
+ color(host.dividerColor)
}
buildThreePidInvites(data)
@@ -130,6 +131,7 @@ class RoomMemberListController @Inject constructor(
}
private fun buildThreePidInvites(data: RoomMemberListViewState) {
+ val host = this
data.threePidInvites()
?.filter { it.content.toModel() != null }
?.join(
@@ -138,11 +140,11 @@ class RoomMemberListController @Inject constructor(
?.let { content ->
profileMatrixItem {
id("3pid_$idx")
- matrixItem(content.toMatrixItem())
- avatarRenderer(avatarRenderer)
+ matrixItem(MatrixItem.UserItem("@", displayName = content.displayName))
+ avatarRenderer(host.avatarRenderer)
editable(data.actionsPermissions.canRevokeThreePidInvite)
clickListener { _ ->
- callback?.onThreePidInviteClicked(event)
+ host.callback?.onThreePidInviteClicked(event)
}
}
}
@@ -150,13 +152,9 @@ class RoomMemberListController @Inject constructor(
between = { idx, _ ->
dividerItem {
id("divider3_$idx")
- color(dividerColor)
+ color(host.dividerColor)
}
}
)
}
-
- private fun RoomThirdPartyInviteContent.toMatrixItem(): MatrixItem {
- return MatrixItem.UserItem("@", displayName = displayName)
- }
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt
index fcc1354542..c510e0501b 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt
@@ -88,6 +88,7 @@ class RoomPermissionsController @Inject constructor(
}
override fun buildModels(data: RoomPermissionsViewState?) {
+ val host = this
buildProfileSection(
stringProvider.getString(R.string.room_permissions_title)
)
@@ -97,17 +98,18 @@ class RoomPermissionsController @Inject constructor(
else -> {
loadingItem {
id("loading")
- loadingText(stringProvider.getString(R.string.loading))
+ loadingText(host.stringProvider.getString(R.string.loading))
}
}
}
}
private fun buildPermissions(data: RoomPermissionsViewState, content: PowerLevelsContent) {
+ val host = this
val editable = data.actionPermissions.canChangePowerLevels
settingsInfoItem {
id("notice")
- helperText(stringProvider.getString(if (editable) R.string.room_permissions_notice else R.string.room_permissions_notice_read_only))
+ helperText(host.stringProvider.getString(if (editable) R.string.room_permissions_notice else R.string.room_permissions_notice_read_only))
}
// Useful permissions
@@ -116,9 +118,9 @@ class RoomPermissionsController @Inject constructor(
// Toggle
formAdvancedToggleItem {
id("showAdvanced")
- title(stringProvider.getString(if (data.showAdvancedPermissions) R.string.hide_advanced else R.string.show_advanced))
+ title(host.stringProvider.getString(if (data.showAdvancedPermissions) R.string.hide_advanced else R.string.show_advanced))
expanded(!data.showAdvancedPermissions)
- listener { callback?.toggleShowAllPermissions() }
+ listener { host.callback?.toggleShowAllPermissions() }
}
// Advanced permissions
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt
index b12a6d52eb..24836bc504 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt
@@ -62,6 +62,7 @@ class RoomSettingsController @Inject constructor(
override fun buildModels(data: RoomSettingsViewState?) {
val roomSummary = data?.roomSummary?.invoke() ?: return
+ val host = this
formEditableAvatarItem {
id("avatar")
@@ -69,7 +70,7 @@ class RoomSettingsController @Inject constructor(
when (val avatarAction = data.avatarAction) {
RoomSettingsViewState.AvatarAction.None -> {
// Use the current value
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
// We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar.
matrixItem(roomSummary.toMatrixItem().copy(avatarUrl = data.currentRoomAvatarUrl))
}
@@ -78,8 +79,8 @@ class RoomSettingsController @Inject constructor(
is RoomSettingsViewState.AvatarAction.UpdateAvatar ->
imageUri(avatarAction.newAvatarUri)
}
- clickListener { callback?.onAvatarChange() }
- deleteListener { callback?.onAvatarDelete() }
+ clickListener { host.callback?.onAvatarChange() }
+ deleteListener { host.callback?.onAvatarDelete() }
}
buildProfileSection(
@@ -90,10 +91,10 @@ class RoomSettingsController @Inject constructor(
id("name")
enabled(data.actionPermissions.canChangeName)
value(data.newName ?: roomSummary.displayName)
- hint(stringProvider.getString(R.string.room_settings_name_hint))
+ hint(host.stringProvider.getString(R.string.room_settings_name_hint))
onTextChange { text ->
- callback?.onNameChanged(text)
+ host.callback?.onNameChanged(text)
}
}
@@ -101,10 +102,10 @@ class RoomSettingsController @Inject constructor(
id("topic")
enabled(data.actionPermissions.canChangeTopic)
value(data.newTopic ?: roomSummary.topic)
- hint(stringProvider.getString(R.string.room_settings_topic_hint))
+ hint(host.stringProvider.getString(R.string.room_settings_topic_hint))
onTextChange { text ->
- callback?.onTopicChanged(text)
+ host.callback?.onTopicChanged(text)
}
}
@@ -134,10 +135,10 @@ class RoomSettingsController @Inject constructor(
// add guest access option?
formSwitchItem {
id("guest_access")
- title(stringProvider.getString(R.string.room_settings_guest_access_title))
+ title(host.stringProvider.getString(R.string.room_settings_guest_access_title))
switchChecked(guestAccess == GuestAccess.CanJoin)
listener {
- callback?.onToggleGuestAccess()
+ host.callback?.onToggleGuestAccess()
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt
index 3867485e6f..2141b6bf27 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt
@@ -21,6 +21,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.net.toUri
+import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@@ -36,6 +37,7 @@ import im.vector.app.databinding.FragmentRoomUploadsBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.roomprofile.RoomProfileArgs
+import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
@@ -76,13 +78,21 @@ class RoomUploadsFragment @Inject constructor(
shareMedia(requireContext(), it.file, getMimeTypeFromUri(requireContext(), it.file.toUri()))
}
is RoomUploadsViewEvents.FileReadyForSaving -> {
- saveMedia(
- context = requireContext(),
- file = it.file,
- title = it.title,
- mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()),
- notificationUtils = notificationUtils
- )
+ lifecycleScope.launch {
+ runCatching {
+ saveMedia(
+ context = requireContext(),
+ file = it.file,
+ title = it.title,
+ mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()),
+ notificationUtils = notificationUtils
+ )
+ }.onFailure { failure ->
+ if (!isAdded) return@onFailure
+ showErrorInSnackbar(failure)
+ }
+ }
+ Unit
}
is RoomUploadsViewEvents.Failure -> showFailure(it.throwable)
}.exhaustive
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/UploadsFileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/UploadsFileController.kt
index f2c0ad2a9d..70240752e2 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/UploadsFileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/UploadsFileController.kt
@@ -49,16 +49,17 @@ class UploadsFileController @Inject constructor(
override fun buildModels(data: RoomUploadsViewState?) {
data ?: return
+ val host = this
buildFileItems(data.fileEvents)
if (data.hasMore) {
loadingItem {
// Always use a different id, because we can be notified several times of visibility state changed
- id("loadMore${idx++}")
+ id("loadMore${host.idx++}")
onVisibilityStateChanged { _, _, visibilityState ->
if (visibilityState == VisibilityState.VISIBLE) {
- listener?.loadMore()
+ host.listener?.loadMore()
}
}
}
@@ -66,24 +67,25 @@ class UploadsFileController @Inject constructor(
}
private fun buildFileItems(fileEvents: List) {
+ val host = this
fileEvents.forEach { uploadEvent ->
uploadsFileItem {
id(uploadEvent.eventId)
title(uploadEvent.contentWithAttachmentContent.body)
- subtitle(stringProvider.getString(R.string.uploads_files_subtitle,
+ subtitle(host.stringProvider.getString(R.string.uploads_files_subtitle,
uploadEvent.senderInfo.disambiguatedDisplayName,
- dateFormatter.format(uploadEvent.root.originServerTs, DateFormatKind.DEFAULT_DATE_AND_TIME)))
+ host.dateFormatter.format(uploadEvent.root.originServerTs, DateFormatKind.DEFAULT_DATE_AND_TIME)))
listener(object : UploadsFileItem.Listener {
override fun onItemClicked() {
- listener?.onOpenClicked(uploadEvent)
+ host.listener?.onOpenClicked(uploadEvent)
}
override fun onDownloadClicked() {
- listener?.onDownloadClicked(uploadEvent)
+ host.listener?.onDownloadClicked(uploadEvent)
}
override fun onShareClicked() {
- listener?.onShareClicked(uploadEvent)
+ host.listener?.onShareClicked(uploadEvent)
}
})
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt
index f8dff345bb..a57d3c45fc 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt
@@ -60,16 +60,17 @@ class UploadsMediaController @Inject constructor(
override fun buildModels(data: RoomUploadsViewState?) {
data ?: return
+ val host = this
buildMediaItems(data.mediaEvents)
if (data.hasMore) {
squareLoadingItem {
// Always use a different id, because we can be notified several times of visibility state changed
- id("loadMore${idx++}")
+ id("loadMore${host.idx++}")
onVisibilityStateChanged { _, _, visibilityState ->
if (visibilityState == VisibilityState.VISIBLE) {
- listener?.loadMore()
+ host.listener?.loadMore()
}
}
}
@@ -77,17 +78,18 @@ class UploadsMediaController @Inject constructor(
}
private fun buildMediaItems(mediaEvents: List) {
+ val host = this
mediaEvents.forEach { uploadEvent ->
when (uploadEvent.contentWithAttachmentContent.msgType) {
MessageType.MSGTYPE_IMAGE -> {
val data = uploadEvent.toImageContentRendererData() ?: return@forEach
uploadsImageItem {
id(uploadEvent.eventId)
- imageContentRenderer(imageContentRenderer)
+ imageContentRenderer(host.imageContentRenderer)
data(data)
listener(object : UploadsImageItem.Listener {
override fun onItemClicked(view: View, data: ImageContentRenderer.Data) {
- listener?.onOpenImageClicked(view, data)
+ host.listener?.onOpenImageClicked(view, data)
}
})
}
@@ -96,11 +98,11 @@ class UploadsMediaController @Inject constructor(
val data = uploadEvent.toVideoContentRendererData() ?: return@forEach
uploadsVideoItem {
id(uploadEvent.eventId)
- imageContentRenderer(imageContentRenderer)
+ imageContentRenderer(host.imageContentRenderer)
data(data)
listener(object : UploadsVideoItem.Listener {
override fun onItemClicked(view: View, data: VideoContentRenderer.Data) {
- listener?.onOpenVideoClicked(view, data)
+ host.listener?.onOpenVideoClicked(view, data)
}
})
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt b/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt
index 7d67946385..81d8be502c 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt
@@ -182,7 +182,7 @@ object VectorLocale {
}
}
// sort by human display names
- .sortedBy { localeToLocalisedString(it).toLowerCase(it) }
+ .sortedBy { localeToLocalisedString(it).lowercase(it) }
supportedLocales.clear()
supportedLocales.addAll(list)
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt
index 334464e304..adab8f8630 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt
@@ -52,7 +52,6 @@ import im.vector.app.features.MainActivityArgs
import im.vector.app.features.workers.signout.SignOutUiWorker
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.failure.isInvalidPassword
@@ -224,7 +223,7 @@ class VectorSettingsGeneralFragment @Inject constructor(
it.summary = TextUtils.formatFileSize(requireContext(), size.toLong())
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
- GlobalScope.launch(Dispatchers.Main) {
+ lifecycleScope.launch(Dispatchers.Main) {
// On UI Thread
displayLoadingView()
diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt
index 6425256929..a98cc35ac2 100644
--- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsController.kt
@@ -42,18 +42,19 @@ class CrossSigningSettingsController @Inject constructor(
override fun buildModels(data: CrossSigningSettingsViewState?) {
if (data == null) return
+ val host = this
when {
data.xSigningKeyCanSign -> {
genericItem {
id("can")
titleIconResourceId(R.drawable.ic_shield_trusted)
- title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete))
+ title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_complete))
}
genericButtonItem {
id("Reset")
- text(stringProvider.getString(R.string.reset_cross_signing))
+ text(host.stringProvider.getString(R.string.reset_cross_signing))
buttonClickAction(DebouncedClickListener({
- interactionListener?.didTapInitializeCrossSigning()
+ host.interactionListener?.didTapInitializeCrossSigning()
}))
}
}
@@ -61,13 +62,13 @@ class CrossSigningSettingsController @Inject constructor(
genericItem {
id("trusted")
titleIconResourceId(R.drawable.ic_shield_custom)
- title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted))
+ title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted))
}
genericButtonItem {
id("Reset")
- text(stringProvider.getString(R.string.reset_cross_signing))
+ text(host.stringProvider.getString(R.string.reset_cross_signing))
buttonClickAction(DebouncedClickListener({
- interactionListener?.didTapInitializeCrossSigning()
+ host.interactionListener?.didTapInitializeCrossSigning()
}))
}
}
@@ -75,27 +76,27 @@ class CrossSigningSettingsController @Inject constructor(
genericItem {
id("enable")
titleIconResourceId(R.drawable.ic_shield_black)
- title(stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted))
+ title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted))
}
genericButtonItem {
id("Reset")
- text(stringProvider.getString(R.string.reset_cross_signing))
+ text(host.stringProvider.getString(R.string.reset_cross_signing))
buttonClickAction(DebouncedClickListener({
- interactionListener?.didTapInitializeCrossSigning()
+ host.interactionListener?.didTapInitializeCrossSigning()
}))
}
}
else -> {
genericItem {
id("not")
- title(stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled))
+ title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled))
}
genericPositiveButtonItem {
id("Initialize")
- text(stringProvider.getString(R.string.initialize_cross_signing))
+ text(host.stringProvider.getString(R.string.initialize_cross_signing))
buttonClickAction(DebouncedClickListener({
- interactionListener?.didTapInitializeCrossSigning()
+ host.interactionListener?.didTapInitializeCrossSigning()
}))
}
}
@@ -112,8 +113,8 @@ class CrossSigningSettingsController @Inject constructor(
+"Master Key:\n"
span {
text = it.unpaddedBase64PublicKey ?: ""
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(12)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(12)
}
}
)
@@ -128,8 +129,8 @@ class CrossSigningSettingsController @Inject constructor(
+"User Key:\n"
span {
text = it.unpaddedBase64PublicKey ?: ""
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(12)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(12)
}
}
)
@@ -144,8 +145,8 @@ class CrossSigningSettingsController @Inject constructor(
+"Self Signed Key:\n"
span {
text = it.unpaddedBase64PublicKey ?: ""
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
- textSize = dimensionConverter.spToPx(12)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ textSize = host.dimensionConverter.spToPx(12)
}
}
)
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt
index b66204a9a4..930ad54b7e 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt
@@ -80,6 +80,7 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
val isMine = data.isMine
val currentSessionIsTrusted = data.accountCrossSigningIsTrusted
Timber.v("handleE2EWithCrossSigning $isMine, $cryptoDeviceInfo, $shield")
+ val host = this
if (isMine) {
if (currentSessionIsTrusted) {
@@ -87,8 +88,8 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield)
- title(stringProvider.getString(R.string.encryption_information_verified))
- description(stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
+ title(host.stringProvider.getString(R.string.encryption_information_verified))
+ description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
}
} else if (data.canVerifySession) {
// You need to complete security, only if there are other session(s) available, or if 4S contains secrets
@@ -96,11 +97,11 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield)
- title(stringProvider.getString(R.string.crosssigning_verify_this_session))
+ title(host.stringProvider.getString(R.string.crosssigning_verify_this_session))
if (data.hasOtherSessions) {
- description(stringProvider.getString(R.string.confirm_your_identity))
+ description(host.stringProvider.getString(R.string.confirm_your_identity))
} else {
- description(stringProvider.getString(R.string.confirm_your_identity_quad_s))
+ description(host.stringProvider.getString(R.string.confirm_your_identity_quad_s))
}
}
}
@@ -116,16 +117,16 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield)
- title(stringProvider.getString(R.string.encryption_information_verified))
- description(stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
+ title(host.stringProvider.getString(R.string.encryption_information_verified))
+ description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
}
} else {
genericItem {
id("trust${cryptoDeviceInfo.deviceId}")
titleIconResourceId(shield)
style(ItemStyle.BIG_TEXT)
- title(stringProvider.getString(R.string.encryption_information_not_verified))
- description(stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc))
+ title(host.stringProvider.getString(R.string.encryption_information_not_verified))
+ description(host.stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc))
}
}
}
@@ -145,12 +146,12 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
}
bottomSheetVerificationActionItem {
id("completeSecurity")
- title(stringProvider.getString(R.string.crosssigning_verify_this_session))
- titleColor(colorProvider.getColor(R.color.riotx_accent))
+ title(host.stringProvider.getString(R.string.crosssigning_verify_this_session))
+ titleColor(host.colorProvider.getColor(R.color.riotx_accent))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColor(R.color.riotx_accent))
+ iconColor(host.colorProvider.getColor(R.color.riotx_accent))
listener {
- callback?.onAction(DevicesAction.CompleteSecurity)
+ host.callback?.onAction(DevicesAction.CompleteSecurity)
}
}
} else if (!isMine) {
@@ -165,6 +166,7 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
}
private fun handleE2EInLegacy(data: DeviceVerificationInfoBottomSheetViewState, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) {
+ val host = this
// ==== Legacy
val isMine = data.isMine
@@ -174,16 +176,16 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield)
- title(stringProvider.getString(R.string.encryption_information_verified))
- description(stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
+ title(host.stringProvider.getString(R.string.encryption_information_verified))
+ description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
}
} else {
genericItem {
id("trust${cryptoDeviceInfo.deviceId}")
titleIconResourceId(shield)
style(ItemStyle.BIG_TEXT)
- title(stringProvider.getString(R.string.encryption_information_not_verified))
- description(stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc))
+ title(host.stringProvider.getString(R.string.encryption_information_not_verified))
+ description(host.stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc))
}
}
@@ -203,29 +205,30 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
}
bottomSheetVerificationActionItem {
id("verify${cryptoDeviceInfo.deviceId}")
- title(stringProvider.getString(R.string.verification_verify_device))
- titleColor(colorProvider.getColor(R.color.riotx_accent))
+ title(host.stringProvider.getString(R.string.verification_verify_device))
+ titleColor(host.colorProvider.getColor(R.color.riotx_accent))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColor(R.color.riotx_accent))
+ iconColor(host.colorProvider.getColor(R.color.riotx_accent))
listener {
- callback?.onAction(DevicesAction.VerifyMyDevice(cryptoDeviceInfo.deviceId))
+ host.callback?.onAction(DevicesAction.VerifyMyDevice(cryptoDeviceInfo.deviceId))
}
}
}
}
private fun addVerifyActions(cryptoDeviceInfo: CryptoDeviceInfo) {
+ val host = this
dividerItem {
id("verifyDiv")
}
bottomSheetVerificationActionItem {
id("verify_text")
- title(stringProvider.getString(R.string.cross_signing_verify_by_text))
- titleColor(colorProvider.getColor(R.color.riotx_accent))
+ title(host.stringProvider.getString(R.string.cross_signing_verify_by_text))
+ titleColor(host.colorProvider.getColor(R.color.riotx_accent))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColor(R.color.riotx_accent))
+ iconColor(host.colorProvider.getColor(R.color.riotx_accent))
listener {
- callback?.onAction(DevicesAction.VerifyMyDeviceManually(cryptoDeviceInfo.deviceId))
+ host.callback?.onAction(DevicesAction.VerifyMyDeviceManually(cryptoDeviceInfo.deviceId))
}
}
dividerItem {
@@ -233,17 +236,18 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
}
bottomSheetVerificationActionItem {
id("verify_emoji")
- title(stringProvider.getString(R.string.cross_signing_verify_by_emoji))
- titleColor(colorProvider.getColor(R.color.riotx_accent))
+ title(host.stringProvider.getString(R.string.cross_signing_verify_by_emoji))
+ titleColor(host.colorProvider.getColor(R.color.riotx_accent))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColor(R.color.riotx_accent))
+ iconColor(host.colorProvider.getColor(R.color.riotx_accent))
listener {
- callback?.onAction(DevicesAction.VerifyMyDevice(cryptoDeviceInfo.deviceId))
+ host.callback?.onAction(DevicesAction.VerifyMyDevice(cryptoDeviceInfo.deviceId))
}
}
}
private fun addGenericDeviceManageActions(data: DeviceVerificationInfoBottomSheetViewState, deviceId: String) {
+ val host = this
// Offer delete session if not me
if (!data.isMine) {
// Add the delete option
@@ -252,12 +256,12 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
}
bottomSheetVerificationActionItem {
id("delete")
- title(stringProvider.getString(R.string.settings_active_sessions_signout_device))
- titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
+ title(host.stringProvider.getString(R.string.settings_active_sessions_signout_device))
+ titleColor(host.colorProvider.getColor(R.color.riotx_destructive_accent))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
+ iconColor(host.colorProvider.getColor(R.color.riotx_destructive_accent))
listener {
- callback?.onAction(DevicesAction.Delete(deviceId))
+ host.callback?.onAction(DevicesAction.Delete(deviceId))
}
}
}
@@ -268,17 +272,18 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
}
bottomSheetVerificationActionItem {
id("rename")
- title(stringProvider.getString(R.string.rename))
- titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
+ title(host.stringProvider.getString(R.string.rename))
+ titleColor(host.colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
iconRes(R.drawable.ic_arrow_right)
- iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
+ iconColor(host.colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
listener {
- callback?.onAction(DevicesAction.PromptRename(deviceId))
+ host.callback?.onAction(DevicesAction.PromptRename(deviceId))
}
}
}
private fun handleNonE2EDevice(data: DeviceVerificationInfoBottomSheetViewState) {
+ val host = this
val info = data.deviceInfo.invoke() ?: return
genericItem {
id("info${info.deviceId}")
@@ -288,7 +293,7 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
genericFooterItem {
id("infoCrypto${info.deviceId}")
- text(stringProvider.getString(R.string.settings_failed_to_get_crypto_device_info))
+ text(host.stringProvider.getString(R.string.settings_failed_to_get_crypto_device_info))
}
info.deviceId?.let { addGenericDeviceManageActions(data, it) }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesController.kt
index ca2fea89d3..a3c7ffaa8f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesController.kt
@@ -61,6 +61,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
}
private fun buildDevicesModels(state: DevicesViewState) {
+ val host = this
when (val devices = state.devices) {
is Loading,
is Uninitialized ->
@@ -70,8 +71,8 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
is Fail ->
errorWithRetryItem {
id("error")
- text(errorFormatter.toHumanReadable(devices.error))
- listener { callback?.retry() }
+ text(host.errorFormatter.toHumanReadable(devices.error))
+ listener { host.callback?.retry() }
}
is Success ->
buildDevicesList(devices(), state.myDeviceId, !state.hasAccountCrossSigning, state.accountCrossSigningIsTrusted)
@@ -82,6 +83,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
myDeviceId: String,
legacyMode: Boolean,
currentSessionCrossTrusted: Boolean) {
+ val host = this
devices
.firstOrNull {
it.deviceInfo.deviceId == myDeviceId
@@ -90,21 +92,21 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
// Current device
genericItemHeader {
id("current")
- text(stringProvider.getString(R.string.devices_current_device))
+ text(host.stringProvider.getString(R.string.devices_current_device))
}
deviceItem {
id("myDevice${deviceInfo.deviceId}")
legacyMode(legacyMode)
trustedSession(currentSessionCrossTrusted)
- dimensionConverter(dimensionConverter)
- colorProvider(colorProvider)
- detailedMode(vectorPreferences.developerMode())
+ dimensionConverter(host.dimensionConverter)
+ colorProvider(host.colorProvider)
+ detailedMode(host.vectorPreferences.developerMode())
deviceInfo(deviceInfo)
currentDevice(true)
e2eCapable(true)
- lastSeenFormatted(dateFormatter.format(deviceInfo.lastSeenTs, DateFormatKind.DEFAULT_DATE_AND_TIME))
- itemClickAction { callback?.onDeviceClicked(deviceInfo) }
+ lastSeenFormatted(host.dateFormatter.format(deviceInfo.lastSeenTs, DateFormatKind.DEFAULT_DATE_AND_TIME))
+ itemClickAction { host.callback?.onDeviceClicked(deviceInfo) }
trusted(DeviceTrustLevel(currentSessionCrossTrusted, true))
}
@@ -126,7 +128,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
if (devices.size > 1) {
genericItemHeader {
id("others")
- text(stringProvider.getString(R.string.devices_other_devices))
+ text(host.stringProvider.getString(R.string.devices_other_devices))
}
devices
@@ -140,12 +142,12 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
id("device$idx")
legacyMode(legacyMode)
trustedSession(currentSessionCrossTrusted)
- dimensionConverter(dimensionConverter)
- colorProvider(colorProvider)
- detailedMode(vectorPreferences.developerMode())
+ dimensionConverter(host.dimensionConverter)
+ colorProvider(host.colorProvider)
+ detailedMode(host.vectorPreferences.developerMode())
deviceInfo(deviceInfo)
currentDevice(false)
- itemClickAction { callback?.onDeviceClicked(deviceInfo) }
+ itemClickAction { host.callback?.onDeviceClicked(deviceInfo) }
e2eCapable(cryptoInfo != null)
trusted(cryptoInfo?.trustLevel)
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt
index 13d7e0f396..8f4e36b9a1 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt
@@ -43,11 +43,12 @@ class AccountDataEpoxyController @Inject constructor(
override fun buildModels(data: AccountDataViewState?) {
if (data == null) return
+ val host = this
when (data.accountData) {
is Loading -> {
loadingItem {
id("loading")
- loadingText(stringProvider.getString(R.string.loading))
+ loadingText(host.stringProvider.getString(R.string.loading))
}
}
is Fail -> {
@@ -61,7 +62,7 @@ class AccountDataEpoxyController @Inject constructor(
if (dataList.isEmpty()) {
genericFooterItem {
id("noResults")
- text(stringProvider.getString(R.string.no_result_placeholder))
+ text(host.stringProvider.getString(R.string.no_result_placeholder))
}
} else {
dataList.forEach { accountData ->
@@ -69,10 +70,10 @@ class AccountDataEpoxyController @Inject constructor(
id(accountData.type)
title(accountData.type)
itemClickAction(DebouncedClickListener({
- interactionListener?.didTap(accountData)
+ host.interactionListener?.didTap(accountData)
}))
itemLongClickAction(View.OnLongClickListener {
- interactionListener?.didLongTap(accountData)
+ host.interactionListener?.didLongTap(accountData)
true
})
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingTrailPagedEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingTrailPagedEpoxyController.kt
index 603c67d074..666b0c3712 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingTrailPagedEpoxyController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingTrailPagedEpoxyController.kt
@@ -54,10 +54,11 @@ class GossipingTrailPagedEpoxyController @Inject constructor(
var interactionListener: InteractionListener? = null
override fun buildItemModel(currentPosition: Int, item: Event?): EpoxyModel<*> {
+ val host = this
val event = item ?: return GenericItem_().apply { id(currentPosition) }
return GenericItem_().apply {
id(event.hashCode())
- itemClickAction(GenericItem.Action("view").apply { perform = Runnable { interactionListener?.didTap(event) } })
+ itemClickAction(GenericItem.Action("view").apply { perform = Runnable { host.interactionListener?.didTap(event) } })
title(
if (event.isEncrypted()) {
"${event.getClearType()} [encrypted]"
@@ -67,7 +68,7 @@ class GossipingTrailPagedEpoxyController @Inject constructor(
)
description(
span {
- +vectorDateFormatter.format(event.ageLocalTs, DateFormatKind.DEFAULT_DATE_AND_TIME)
+ +host.vectorDateFormatter.format(event.ageLocalTs, DateFormatKind.DEFAULT_DATE_AND_TIME)
span("\nfrom: ") {
textStyle = "bold"
}
@@ -98,7 +99,7 @@ class GossipingTrailPagedEpoxyController @Inject constructor(
val content = event.getClearContent().toModel()
if (event.mxDecryptionResult == null) {
span("**Failed to Decrypt** ${event.mCryptoError}") {
- textColor = colorProvider.getColor(R.color.vector_error_color)
+ textColor = host.colorProvider.getColor(R.color.vector_error_color)
}
}
span("\nsessionId:") {
@@ -157,7 +158,7 @@ class GossipingTrailPagedEpoxyController @Inject constructor(
+"${content?.requestingDeviceId}"
} else if (event.getClearType() == EventType.ENCRYPTED) {
span("**Failed to Decrypt** ${event.mCryptoError}") {
- textColor = colorProvider.getColor(R.color.vector_error_color)
+ textColor = host.colorProvider.getColor(R.color.vector_error_color)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestPagedController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestPagedController.kt
index c2bdcfbd04..3c90a45237 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestPagedController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestPagedController.kt
@@ -40,6 +40,7 @@ class IncomingKeyRequestPagedController @Inject constructor(
var interactionListener: InteractionListener? = null
override fun buildItemModel(currentPosition: Int, item: IncomingRoomKeyRequest?): EpoxyModel<*> {
+ val host = this
val roomKeyRequest = item ?: return GenericItem_().apply { id(currentPosition) }
return GenericItem_().apply {
@@ -51,7 +52,7 @@ class IncomingKeyRequestPagedController @Inject constructor(
textStyle = "bold"
}
span("${roomKeyRequest.userId}")
- +vectorDateFormatter.format(roomKeyRequest.localCreationTimestamp, DateFormatKind.DEFAULT_DATE_AND_TIME)
+ +host.vectorDateFormatter.format(roomKeyRequest.localCreationTimestamp, DateFormatKind.DEFAULT_DATE_AND_TIME)
span("\nsessionId:") {
textStyle = "bold"
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt
index 514311315d..3217756a82 100644
--- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt
@@ -46,6 +46,7 @@ class HomeserverSettingsController @Inject constructor(
override fun buildModels(data: HomeServerSettingsViewState?) {
data ?: return
+ val host = this
buildHeader(data)
buildCapabilities(data)
@@ -58,8 +59,8 @@ class HomeserverSettingsController @Inject constructor(
is Fail ->
errorWithRetryItem {
id("error")
- text(errorFormatter.toHumanReadable(federationVersion.error))
- listener { callback?.retry() }
+ text(host.errorFormatter.toHumanReadable(federationVersion.error))
+ listener { host.callback?.retry() }
}
is Success ->
buildFederationVersion(federationVersion())
@@ -101,6 +102,7 @@ class HomeserverSettingsController @Inject constructor(
}
private fun buildCapabilities(data: HomeServerSettingsViewState) {
+ val host = this
settingsSectionTitleItem {
id("uploadTitle")
titleResId(R.string.settings_server_upload_size_title)
@@ -113,7 +115,7 @@ class HomeserverSettingsController @Inject constructor(
if (limit == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
helperTextResId(R.string.settings_server_upload_size_unknown)
} else {
- helperText(stringProvider.getString(R.string.settings_server_upload_size_content, "${limit / 1048576L} MB"))
+ helperText(host.stringProvider.getString(R.string.settings_server_upload_size_content, "${limit / 1048576L} MB"))
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt
index 8080f2e032..ce84366ef3 100644
--- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt
@@ -46,18 +46,19 @@ class IgnoredUsersController @Inject constructor(private val stringProvider: Str
}
private fun buildIgnoredUserModels(users: List) {
+ val host = this
if (users.isEmpty()) {
noResultItem {
id("empty")
- text(stringProvider.getString(R.string.no_ignored_users))
+ text(host.stringProvider.getString(R.string.no_ignored_users))
}
} else {
users.forEach { user ->
userItem {
id(user.userId)
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
matrixItem(user.toMatrixItem())
- itemClickAction { callback?.onUserIdClicked(user.userId) }
+ itemClickAction { host.callback?.onUserIdClicked(user.userId) }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt
index 9654eb2190..55d0ed7c3f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt
@@ -24,6 +24,7 @@ import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.epoxy.profiles.profileSectionItem
import im.vector.app.core.resources.StringProvider
+import im.vector.app.core.utils.safeCapitalize
import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences
import java.util.Locale
@@ -39,35 +40,36 @@ class LocalePickerController @Inject constructor(
@ExperimentalStdlibApi
override fun buildModels(data: LocalePickerViewState?) {
val list = data?.locales ?: return
+ val host = this
profileSectionItem {
id("currentTitle")
- title(stringProvider.getString(R.string.choose_locale_current_locale_title))
+ title(host.stringProvider.getString(R.string.choose_locale_current_locale_title))
}
localeItem {
id(data.currentLocale.toString())
- title(VectorLocale.localeToLocalisedString(data.currentLocale).capitalize(data.currentLocale))
- if (vectorPreferences.developerMode()) {
+ title(VectorLocale.localeToLocalisedString(data.currentLocale).safeCapitalize(data.currentLocale))
+ if (host.vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(data.currentLocale))
}
- clickListener { listener?.onUseCurrentClicked() }
+ clickListener { host.listener?.onUseCurrentClicked() }
}
profileSectionItem {
id("otherTitle")
- title(stringProvider.getString(R.string.choose_locale_other_locales_title))
+ title(host.stringProvider.getString(R.string.choose_locale_other_locales_title))
}
when (list) {
is Incomplete -> {
loadingItem {
id("loading")
- loadingText(stringProvider.getString(R.string.choose_locale_loading_locales))
+ loadingText(host.stringProvider.getString(R.string.choose_locale_loading_locales))
}
}
is Success ->
if (list().isEmpty()) {
noResultItem {
id("noResult")
- text(stringProvider.getString(R.string.no_result_placeholder))
+ text(host.stringProvider.getString(R.string.no_result_placeholder))
}
} else {
list()
@@ -75,11 +77,11 @@ class LocalePickerController @Inject constructor(
.forEach {
localeItem {
id(it.toString())
- title(VectorLocale.localeToLocalisedString(it).capitalize(it))
- if (vectorPreferences.developerMode()) {
+ title(VectorLocale.localeToLocalisedString(it).safeCapitalize(it))
+ if (host.vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(it))
}
- clickListener { listener?.onLocaleClicked(it) }
+ clickListener { host.listener?.onLocaleClicked(it) }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt
index 2d111e4424..679f406832 100644
--- a/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt
@@ -27,11 +27,12 @@ class PushGateWayController @Inject constructor(
) : TypedEpoxyController() {
override fun buildModels(data: PushGatewayViewState?) {
+ val host = this
data?.pushGateways?.invoke()?.let { pushers ->
if (pushers.isEmpty()) {
genericFooterItem {
id("footer")
- text(stringProvider.getString(R.string.settings_push_gateway_no_pushers))
+ text(host.stringProvider.getString(R.string.settings_push_gateway_no_pushers))
}
} else {
pushers.forEach {
@@ -44,7 +45,7 @@ class PushGateWayController @Inject constructor(
} ?: run {
genericFooterItem {
id("loading")
- text(stringProvider.getString(R.string.loading))
+ text(host.stringProvider.getString(R.string.loading))
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt
index a8a1ab2e17..c0119ed3be 100644
--- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt
@@ -27,6 +27,7 @@ class PushRulesController @Inject constructor(
) : TypedEpoxyController() {
override fun buildModels(data: PushRulesViewState?) {
+ val host = this
data?.let {
it.rules.forEach {
pushRuleItem {
@@ -37,7 +38,7 @@ class PushRulesController @Inject constructor(
} ?: run {
genericFooterItem {
id("footer")
- text(stringProvider.getString(R.string.settings_push_rules_no_rules))
+ text(host.stringProvider.getString(R.string.settings_push_rules_no_rules))
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt
index cf811f1611..cb2cb3382f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt
@@ -71,6 +71,7 @@ class ThreePidsSettingsController @Inject constructor(
override fun buildModels(data: ThreePidsSettingsViewState?) {
if (data == null) return
+ val host = this
if (data.uiState is ThreePidsSettingsUiState.Idle) {
currentInputValue = ""
@@ -80,7 +81,7 @@ class ThreePidsSettingsController @Inject constructor(
is Loading -> {
loadingItem {
id("loading")
- loadingText(stringProvider.getString(R.string.loading))
+ loadingText(host.stringProvider.getString(R.string.loading))
}
}
is Fail -> {
@@ -97,13 +98,14 @@ class ThreePidsSettingsController @Inject constructor(
}
private fun buildThreePids(list: List, data: ThreePidsSettingsViewState) {
+ val host = this
val splited = list.groupBy { it is ThreePid.Email }
val emails = splited[true].orEmpty()
val msisdn = splited[false].orEmpty()
settingsSectionTitleItem {
id("email")
- title(stringProvider.getString(R.string.settings_emails))
+ title(host.stringProvider.getString(R.string.settings_emails))
}
emails.forEach { buildThreePid("email ", it) }
@@ -116,7 +118,7 @@ class ThreePidsSettingsController @Inject constructor(
if (pendingList.isEmpty() && emails.isEmpty()) {
noResultItem {
id("noEmail")
- text(stringProvider.getString(R.string.settings_emails_empty))
+ text(host.stringProvider.getString(R.string.settings_emails_empty))
}
}
@@ -127,15 +129,15 @@ class ThreePidsSettingsController @Inject constructor(
ThreePidsSettingsUiState.Idle ->
genericButtonItem {
id("addEmail")
- text(stringProvider.getString(R.string.settings_add_email_address))
- textColor(colorProvider.getColor(R.color.riotx_accent))
- buttonClickAction(View.OnClickListener { interactionListener?.addEmail() })
+ text(host.stringProvider.getString(R.string.settings_add_email_address))
+ textColor(host.colorProvider.getColor(R.color.riotx_accent))
+ buttonClickAction(View.OnClickListener { host.interactionListener?.addEmail() })
}
is ThreePidsSettingsUiState.AddingEmail -> {
settingsEditTextItem {
id("addingEmail")
inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS)
- hint(stringProvider.getString(R.string.medium_email))
+ hint(host.stringProvider.getString(R.string.medium_email))
if (data.editTextReinitiator.isTrue()) {
value("")
requestFocus(true)
@@ -143,18 +145,18 @@ class ThreePidsSettingsController @Inject constructor(
errorText(data.uiState.error)
interactionListener(object : SettingsEditTextItem.Listener {
override fun onValidate() {
- interactionListener?.doAddEmail(currentInputValue)
+ host.interactionListener?.doAddEmail(host.currentInputValue)
}
override fun onTextChange(text: String) {
- currentInputValue = text
+ host.currentInputValue = text
}
})
}
settingsContinueCancelItem {
id("contAddingEmail")
- continueOnClick { interactionListener?.doAddEmail(currentInputValue) }
- cancelOnClick { interactionListener?.cancelAdding() }
+ continueOnClick { host.interactionListener?.doAddEmail(host.currentInputValue) }
+ cancelOnClick { host.interactionListener?.cancelAdding() }
}
}
is ThreePidsSettingsUiState.AddingPhoneNumber -> Unit
@@ -162,7 +164,7 @@ class ThreePidsSettingsController @Inject constructor(
settingsSectionTitleItem {
id("msisdn")
- title(stringProvider.getString(R.string.settings_phone_numbers))
+ title(host.stringProvider.getString(R.string.settings_phone_numbers))
}
msisdn.forEach { buildThreePid("msisdn ", it) }
@@ -175,7 +177,7 @@ class ThreePidsSettingsController @Inject constructor(
if (pendingList.isEmpty() && msisdn.isEmpty()) {
noResultItem {
id("noMsisdn")
- text(stringProvider.getString(R.string.settings_phone_number_empty))
+ text(host.stringProvider.getString(R.string.settings_phone_number_empty))
}
}
@@ -186,20 +188,20 @@ class ThreePidsSettingsController @Inject constructor(
ThreePidsSettingsUiState.Idle ->
genericButtonItem {
id("addMsisdn")
- text(stringProvider.getString(R.string.settings_add_phone_number))
- textColor(colorProvider.getColor(R.color.riotx_accent))
- buttonClickAction(View.OnClickListener { interactionListener?.addMsisdn() })
+ text(host.stringProvider.getString(R.string.settings_add_phone_number))
+ textColor(host.colorProvider.getColor(R.color.riotx_accent))
+ buttonClickAction(View.OnClickListener { host.interactionListener?.addMsisdn() })
}
is ThreePidsSettingsUiState.AddingEmail -> Unit
is ThreePidsSettingsUiState.AddingPhoneNumber -> {
settingsInfoItem {
id("addingMsisdnInfo")
- helperText(stringProvider.getString(R.string.login_msisdn_notice))
+ helperText(host.stringProvider.getString(R.string.login_msisdn_notice))
}
settingsEditTextItem {
id("addingMsisdn")
inputType(InputType.TYPE_CLASS_PHONE)
- hint(stringProvider.getString(R.string.medium_phone_number))
+ hint(host.stringProvider.getString(R.string.medium_phone_number))
if (data.editTextReinitiator.isTrue()) {
value("")
requestFocus(true)
@@ -207,34 +209,36 @@ class ThreePidsSettingsController @Inject constructor(
errorText(data.uiState.error)
interactionListener(object : SettingsEditTextItem.Listener {
override fun onValidate() {
- interactionListener?.doAddMsisdn(currentInputValue)
+ host.interactionListener?.doAddMsisdn(host.currentInputValue)
}
override fun onTextChange(text: String) {
- currentInputValue = text
+ host.currentInputValue = text
}
})
}
settingsContinueCancelItem {
id("contAddingMsisdn")
- continueOnClick { interactionListener?.doAddMsisdn(currentInputValue) }
- cancelOnClick { interactionListener?.cancelAdding() }
+ continueOnClick { host.interactionListener?.doAddMsisdn(host.currentInputValue) }
+ cancelOnClick { host.interactionListener?.cancelAdding() }
}
}
}.exhaustive
}
private fun buildThreePid(idPrefix: String, threePid: ThreePid) {
+ val host = this
threePidItem {
id(idPrefix + threePid.value)
// TODO Add an icon for emails
// iconResId(if (threePid is ThreePid.Msisdn) R.drawable.ic_phone else null)
title(threePid.getFormattedValue())
- deleteClickListener { interactionListener?.deleteThreePid(threePid) }
+ deleteClickListener { host.interactionListener?.deleteThreePid(threePid) }
}
}
private fun buildPendingThreePid(data: ThreePidsSettingsViewState, idPrefix: String, threePid: ThreePid) {
+ val host = this
threePidItem {
id(idPrefix + threePid.value)
// TODO Add an icon for emails
@@ -246,43 +250,43 @@ class ThreePidsSettingsController @Inject constructor(
is ThreePid.Email -> {
settingsInformationItem {
id("info" + idPrefix + threePid.value)
- message(stringProvider.getString(R.string.account_email_validation_message))
- colorProvider(colorProvider)
+ message(host.stringProvider.getString(R.string.account_email_validation_message))
+ colorProvider(host.colorProvider)
}
settingsContinueCancelItem {
id("cont" + idPrefix + threePid.value)
- continueOnClick { interactionListener?.continueThreePid(threePid) }
- cancelOnClick { interactionListener?.cancelThreePid(threePid) }
+ continueOnClick { host.interactionListener?.continueThreePid(threePid) }
+ cancelOnClick { host.interactionListener?.cancelThreePid(threePid) }
}
}
is ThreePid.Msisdn -> {
settingsInformationItem {
id("info" + idPrefix + threePid.value)
- message(stringProvider.getString(R.string.settings_text_message_sent, threePid.getFormattedValue()))
- colorProvider(colorProvider)
+ message(host.stringProvider.getString(R.string.settings_text_message_sent, threePid.getFormattedValue()))
+ colorProvider(host.colorProvider)
}
settingsEditTextItem {
id("msisdnVerification${threePid.value}")
inputType(InputType.TYPE_CLASS_NUMBER)
- hint(stringProvider.getString(R.string.settings_text_message_sent_hint))
+ hint(host.stringProvider.getString(R.string.settings_text_message_sent_hint))
if (data.msisdnValidationReinitiator[threePid]?.isTrue() == true) {
value("")
}
- errorText(getCodeError(data, threePid))
+ errorText(host.getCodeError(data, threePid))
interactionListener(object : SettingsEditTextItem.Listener {
override fun onValidate() {
- interactionListener?.submitCode(threePid, currentCodes[threePid] ?: "")
+ host.interactionListener?.submitCode(threePid, host.currentCodes[threePid] ?: "")
}
override fun onTextChange(text: String) {
- currentCodes[threePid] = text
+ host.currentCodes[threePid] = text
}
})
}
settingsContinueCancelItem {
id("cont" + idPrefix + threePid.value)
- continueOnClick { interactionListener?.submitCode(threePid, currentCodes[threePid] ?: "") }
- cancelOnClick { interactionListener?.cancelThreePid(threePid) }
+ continueOnClick { host.interactionListener?.submitCode(threePid, host.currentCodes[threePid] ?: "") }
+ cancelOnClick { host.interactionListener?.cancelThreePid(threePid) }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt
index 35c6de24c7..fd35bf11a4 100644
--- a/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt
+++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt
@@ -37,6 +37,7 @@ class IncomingShareController @Inject constructor(private val roomSummaryItemFac
var callback: Callback? = null
override fun buildModels(data: IncomingShareViewState) {
+ val host = this
if (data.sharedData == null || data.filteredRoomSummaries is Incomplete) {
loadingItem {
id("loading")
@@ -47,7 +48,7 @@ class IncomingShareController @Inject constructor(private val roomSummaryItemFac
if (roomSummaries.isNullOrEmpty()) {
noResultItem {
id("no_result")
- text(stringProvider.getString(R.string.no_result_placeholder))
+ text(host.stringProvider.getString(R.string.no_result_placeholder))
}
} else {
roomSummaries.forEach { roomSummary ->
diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt
index f89480046f..7b5d8a9ebd 100644
--- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt
@@ -41,7 +41,6 @@ import im.vector.app.databinding.FragmentIncomingShareBinding
import im.vector.app.features.attachments.AttachmentsHelper
import im.vector.app.features.attachments.preview.AttachmentsPreviewActivity
import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs
-import im.vector.app.features.login.LoginActivity
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -211,9 +210,10 @@ class IncomingShareFragment @Inject constructor(
}
private fun startLoginActivity() {
- val intent = LoginActivity.newIntent(requireActivity(), null)
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
- startActivity(intent)
+ navigator.openLogin(
+ context = requireActivity(),
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
+ )
requireActivity().finish()
}
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt
new file mode 100644
index 0000000000..cfccc6f699
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.signout.soft
+
+import android.content.Context
+import android.content.Intent
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
+import androidx.fragment.app.FragmentManager
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.viewModel
+import im.vector.app.R
+import im.vector.app.core.di.ScreenComponent
+import im.vector.app.core.error.ErrorFormatter
+import im.vector.app.core.extensions.replaceFragment
+import im.vector.app.features.MainActivity
+import im.vector.app.features.MainActivityArgs
+import im.vector.app.features.login2.LoginActivity2
+
+import org.matrix.android.sdk.api.failure.GlobalError
+import org.matrix.android.sdk.api.session.Session
+import timber.log.Timber
+import javax.inject.Inject
+
+/**
+ * In this screen, the user is viewing a message informing that he has been logged out
+ * Extends LoginActivity to get the login with SSO and forget password functionality for (nearly) free
+ *
+ * This is just a copy of SoftLogoutActivity2, which extends LoginActivity2
+ */
+class SoftLogoutActivity2 : LoginActivity2() {
+
+ private val softLogoutViewModel: SoftLogoutViewModel by viewModel()
+
+ @Inject lateinit var softLogoutViewModelFactory: SoftLogoutViewModel.Factory
+ @Inject lateinit var session: Session
+ @Inject lateinit var errorFormatter: ErrorFormatter
+
+ override fun injectWith(injector: ScreenComponent) {
+ super.injectWith(injector)
+ injector.inject(this)
+ }
+
+ override fun initUiAndData() {
+ super.initUiAndData()
+
+ softLogoutViewModel.subscribe(this) {
+ updateWithState(it)
+ }
+
+ softLogoutViewModel.observeViewEvents { handleSoftLogoutViewEvents(it) }
+ }
+
+ private fun handleSoftLogoutViewEvents(softLogoutViewEvents: SoftLogoutViewEvents) {
+ when (softLogoutViewEvents) {
+ is SoftLogoutViewEvents.Failure ->
+ showError(errorFormatter.toHumanReadable(softLogoutViewEvents.throwable))
+ is SoftLogoutViewEvents.ErrorNotSameUser -> {
+ // Pop the backstack
+ supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+
+ // And inform the user
+ showError(getString(
+ R.string.soft_logout_sso_not_same_user_error,
+ softLogoutViewEvents.currentUserId,
+ softLogoutViewEvents.newUserId)
+ )
+ }
+ is SoftLogoutViewEvents.ClearData -> {
+ MainActivity.restartApp(this, MainActivityArgs(clearCredentials = true))
+ }
+ }
+ }
+
+ private fun showError(message: String) {
+ AlertDialog.Builder(this)
+ .setTitle(R.string.dialog_title_error)
+ .setMessage(message)
+ .setPositiveButton(R.string.ok, null)
+ .show()
+ }
+
+ override fun addFirstFragment() {
+ replaceFragment(R.id.loginFragmentContainer, SoftLogoutFragment::class.java)
+ }
+
+ private fun updateWithState(softLogoutViewState: SoftLogoutViewState) {
+ if (softLogoutViewState.asyncLoginAction is Success) {
+ MainActivity.restartApp(this, MainActivityArgs())
+ }
+
+ views.loginLoading.isVisible = softLogoutViewState.isLoading()
+ }
+
+ companion object {
+ fun newIntent(context: Context): Intent {
+ return Intent(context, SoftLogoutActivity2::class.java)
+ }
+ }
+
+ override fun handleInvalidToken(globalError: GlobalError.InvalidToken) {
+ // No op here
+ Timber.w("Ignoring invalid token global error")
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
index 89fa4a982a..76f0fd7fd2 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
@@ -65,20 +65,21 @@ class SoftLogoutController @Inject constructor(
}
private fun buildHeader(state: SoftLogoutViewState) {
+ val host = this
loginHeaderItem {
id("header")
}
loginTitleItem {
id("title")
- text(stringProvider.getString(R.string.soft_logout_title))
+ text(host.stringProvider.getString(R.string.soft_logout_title))
}
loginTitleSmallItem {
id("signTitle")
- text(stringProvider.getString(R.string.soft_logout_signin_title))
+ text(host.stringProvider.getString(R.string.soft_logout_signin_title))
}
loginTextItem {
id("signText1")
- text(stringProvider.getString(R.string.soft_logout_signin_notice,
+ text(host.stringProvider.getString(R.string.soft_logout_signin_notice,
state.homeServerUrl.toReducedUrl(),
state.userDisplayName,
state.userId))
@@ -86,12 +87,13 @@ class SoftLogoutController @Inject constructor(
if (state.hasUnsavedKeys) {
loginTextItem {
id("signText2")
- text(stringProvider.getString(R.string.soft_logout_signin_e2e_warning_notice))
+ text(host.stringProvider.getString(R.string.soft_logout_signin_e2e_warning_notice))
}
}
}
private fun buildForm(state: SoftLogoutViewState) {
+ val host = this
when (state.asyncHomeServerLoginFlowRequest) {
is Incomplete -> {
loadingItem {
@@ -101,8 +103,8 @@ class SoftLogoutController @Inject constructor(
is Fail -> {
loginErrorWithRetryItem {
id("errorRetry")
- text(errorFormatter.toHumanReadable(state.asyncHomeServerLoginFlowRequest.error))
- listener { listener?.retry() }
+ text(host.errorFormatter.toHumanReadable(state.asyncHomeServerLoginFlowRequest.error))
+ listener { host.listener?.retry() }
}
}
is Success -> {
@@ -110,21 +112,21 @@ class SoftLogoutController @Inject constructor(
LoginMode.Password -> {
loginPasswordFormItem {
id("passwordForm")
- stringProvider(stringProvider)
+ stringProvider(host.stringProvider)
passwordShown(state.passwordShown)
submitEnabled(state.submitEnabled)
- onPasswordEdited { listener?.passwordEdited(it) }
- errorText((state.asyncLoginAction as? Fail)?.error?.let { errorFormatter.toHumanReadable(it) })
- passwordRevealClickListener { listener?.revealPasswordClicked() }
- forgetPasswordClickListener { listener?.forgetPasswordClicked() }
- submitClickListener { password -> listener?.signinSubmit(password) }
+ onPasswordEdited { host.listener?.passwordEdited(it) }
+ errorText((state.asyncLoginAction as? Fail)?.error?.let { host.errorFormatter.toHumanReadable(it) })
+ passwordRevealClickListener { host.listener?.revealPasswordClicked() }
+ forgetPasswordClickListener { host.listener?.forgetPasswordClicked() }
+ submitClickListener { password -> host.listener?.signinSubmit(password) }
}
}
is LoginMode.Sso -> {
loginCenterButtonItem {
id("sso")
- text(stringProvider.getString(R.string.login_signin_sso))
- listener { listener?.signinFallbackSubmit() }
+ text(host.stringProvider.getString(R.string.login_signin_sso))
+ listener { host.listener?.signinFallbackSubmit() }
}
}
is LoginMode.SsoAndPassword -> {
@@ -132,8 +134,8 @@ class SoftLogoutController @Inject constructor(
LoginMode.Unsupported -> {
loginCenterButtonItem {
id("fallback")
- text(stringProvider.getString(R.string.login_signin))
- listener { listener?.signinFallbackSubmit() }
+ text(host.stringProvider.getString(R.string.login_signin))
+ listener { host.listener?.signinFallbackSubmit() }
}
}
LoginMode.Unknown -> Unit // Should not happen
@@ -143,18 +145,19 @@ class SoftLogoutController @Inject constructor(
}
private fun buildClearDataSection() {
+ val host = this
loginTitleSmallItem {
id("clearDataTitle")
- text(stringProvider.getString(R.string.soft_logout_clear_data_title))
+ text(host.stringProvider.getString(R.string.soft_logout_clear_data_title))
}
loginTextItem {
id("clearDataText")
- text(stringProvider.getString(R.string.soft_logout_clear_data_notice))
+ text(host.stringProvider.getString(R.string.soft_logout_clear_data_notice))
}
loginRedButtonItem {
id("clearDataSubmit")
- text(stringProvider.getString(R.string.soft_logout_clear_data_submit))
- listener { listener?.clearData() }
+ text(host.stringProvider.getString(R.string.soft_logout_clear_data_submit))
+ listener { host.listener?.clearData() }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/ShareSpaceBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/ShareSpaceBottomSheet.kt
deleted file mode 100644
index 2f69ba89b9..0000000000
--- a/vector/src/main/java/im/vector/app/features/spaces/ShareSpaceBottomSheet.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2021 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.spaces
-
-import android.os.Bundle
-import android.os.Parcelable
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.view.isVisible
-import androidx.fragment.app.FragmentManager
-import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
-import im.vector.app.core.di.ScreenComponent
-import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
-import im.vector.app.core.utils.startSharePlainTextIntent
-import im.vector.app.databinding.BottomSheetSpaceInviteBinding
-import im.vector.app.features.invite.InviteUsersToRoomActivity
-import kotlinx.parcelize.Parcelize
-import javax.inject.Inject
-
-class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment() {
-
- @Parcelize
- data class Args(
- val spaceId: String
- ) : Parcelable
-
- override val showExpanded = true
-
- @Inject
- lateinit var activeSessionHolder: ActiveSessionHolder
-
- override fun injectWith(injector: ScreenComponent) {
- injector.inject(this)
- }
-
- override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetSpaceInviteBinding {
- return BottomSheetSpaceInviteBinding.inflate(inflater, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- // Not going for full view model for now, as it may change
-
- val args: Args = arguments?.getParcelable(EXTRA_ARGS)
- ?: return Unit.also { dismiss() }
- val summary = activeSessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(args.spaceId)?.spaceSummary()
-
- val spaceName = summary?.name
- views.descriptionText.text = getString(R.string.invite_people_to_your_space_desc, spaceName)
-
- // XXX enable back when supported
- views.inviteByMailButton.isVisible = false
- views.inviteByMailButton.debouncedClicks {
- }
-
- views.inviteByMxidButton.debouncedClicks {
- val intent = InviteUsersToRoomActivity.getIntent(requireContext(), args.spaceId)
- startActivity(intent)
- }
-
- views.inviteByLinkButton.debouncedClicks {
- activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createRoomPermalink(args.spaceId)?.let { permalink ->
- startSharePlainTextIntent(
- fragment = this,
- activityResultLauncher = null,
- chooserTitle = getString(R.string.share_by_text),
- text = getString(R.string.share_space_link_message, spaceName, permalink),
- extraTitle = getString(R.string.share_space_link_message, spaceName, permalink)
- )
- }
- }
-
-// views.skipButton.debouncedClicks {
-// dismiss()
-// }
- }
-
- companion object {
-
- const val EXTRA_ARGS = "EXTRA_ARGS"
-
- fun show(fragmentManager: FragmentManager, spaceId: String): ShareSpaceBottomSheet {
- return ShareSpaceBottomSheet().apply {
- isCancelable = true
- arguments = Bundle().apply {
- this.putParcelable(EXTRA_ARGS, ShareSpaceBottomSheet.Args(spaceId = spaceId))
- }
- }.also {
- it.show(fragmentManager, ShareSpaceBottomSheet::class.java.name)
- }
- }
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
index 8271595d9d..2526b62f39 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.spaces
+import android.content.DialogInterface
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
@@ -27,8 +28,10 @@ import com.airbnb.mvrx.args
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
+import im.vector.app.core.dialogs.withColoredButton
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
+import im.vector.app.core.resources.ColorProvider
import im.vector.app.databinding.BottomSheetSpaceSettingsBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator
@@ -36,15 +39,18 @@ import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.roomprofile.RoomProfileActivity
+import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceManageActivity
import io.reactivex.android.schedulers.AndroidSchedulers
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
+import me.gujun.android.span.span
+import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
+import org.matrix.android.sdk.api.session.room.powerlevels.Role
import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber
import javax.inject.Inject
@@ -54,6 +60,7 @@ data class SpaceBottomSheetSettingsArgs(
val spaceId: String
) : Parcelable
+// XXX make proper view model before leaving beta
class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() {
@Inject lateinit var navigator: Navigator
@@ -61,6 +68,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment
- GlobalScope.launch {
+ session.coroutineScope.launch {
try {
session.getRoom(spaceArgs.spaceId)?.leave(null)
} catch (failure: Throwable) {
@@ -153,6 +191,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment
groupSummaryItem {
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
id("all_communities")
- matrixItem(mxItem.copy(displayName = stringProvider.getString(R.string.group_all_communities)))
+ matrixItem(mxItem.copy(displayName = host.stringProvider.getString(R.string.group_all_communities)))
selected(nonNullViewState.selectedGroupingMethod is RoomGroupingMethod.ByLegacyGroup
&& nonNullViewState.selectedGroupingMethod.group() == null)
- listener { callback?.onGroupSelected(null) }
+ listener { host.callback?.onGroupSelected(null) }
}
}
nonNullViewState.legacyGroups.forEach { groupSummary ->
groupSummaryItem {
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
id(groupSummary.groupId)
matrixItem(groupSummary.toMatrixItem())
selected(nonNullViewState.selectedGroupingMethod is RoomGroupingMethod.ByLegacyGroup
&& nonNullViewState.selectedGroupingMethod.group()?.groupId == groupSummary.groupId)
- listener { callback?.onGroupSelected(groupSummary) }
+ listener { host.callback?.onGroupSelected(groupSummary) }
}
}
}
@@ -111,10 +112,11 @@ class SpaceSummaryController @Inject constructor(
rootSpaces: List?,
expandedStates: Map,
homeCount: RoomAggregateNotificationCount) {
+ val host = this
spaceBetaHeaderItem {
id("beta_header")
clickAction(View.OnClickListener {
- callback?.sendFeedBack()
+ host.callback?.sendFeedBack()
})
}
@@ -123,13 +125,13 @@ class SpaceSummaryController @Inject constructor(
summaries?.filter { it.membership == Membership.INVITE }
?.forEach {
spaceSummaryItem {
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
id(it.roomId)
matrixItem(it.toMatrixItem())
countState(UnreadCounterBadgeView.State(1, true, 0, false))
selected(false)
- description(stringProvider.getString(R.string.you_are_invited))
- listener { callback?.onSpaceInviteSelected(it) }
+ description(host.stringProvider.getString(R.string.you_are_invited))
+ listener { host.callback?.onSpaceInviteSelected(it) }
}
}
@@ -137,11 +139,11 @@ class SpaceSummaryController @Inject constructor(
id("space_home")
selected(selected is RoomGroupingMethod.BySpace && selected.space() == null)
countState(UnreadCounterBadgeView.State(homeCount.totalCount, homeCount.isHighlight, homeCount.unreadCount, homeCount.markedUnread))
- listener { callback?.onSpaceSelected(null) }
+ listener { host.callback?.onSpaceSelected(null) }
}
rootSpaces
- ?.sortedBy { it.displayName.toLowerCase(Locale.getDefault()) }
+ ?.sortedBy { it.displayName.lowercase(Locale.getDefault()) }
?.forEach { groupSummary ->
val isSelected = selected is RoomGroupingMethod.BySpace && groupSummary.roomId == selected.space()?.roomId
// does it have children?
@@ -152,20 +154,20 @@ class SpaceSummaryController @Inject constructor(
val expanded = expandedStates[groupSummary.roomId] == true
spaceSummaryItem {
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
id(groupSummary.roomId)
hasChildren(hasChildren)
expanded(expanded)
matrixItem(groupSummary.toMatrixItem())
selected(isSelected)
- onMore { callback?.onSpaceSettings(groupSummary) }
- listener { callback?.onSpaceSelected(groupSummary) }
- toggleExpand { callback?.onToggleExpand(groupSummary) }
+ onMore { host.callback?.onSpaceSettings(groupSummary) }
+ listener { host.callback?.onSpaceSelected(groupSummary) }
+ toggleExpand { host.callback?.onToggleExpand(groupSummary) }
countState(
UnreadCounterBadgeView.State(
groupSummary.notificationCount,
groupSummary.highlightCount > 0,
- groupSummary.scUnreadCount(scSdkPreferences),
+ groupSummary.scUnreadCount(host.scSdkPreferences),
groupSummary.markedUnread
)
)
@@ -181,7 +183,7 @@ class SpaceSummaryController @Inject constructor(
spaceAddItem {
id("create")
- listener { callback?.onAddSpaceSelected() }
+ listener { host.callback?.onAddSpaceSelected() }
}
}
@@ -189,6 +191,7 @@ class SpaceSummaryController @Inject constructor(
expandedStates: Map,
selected: RoomGroupingMethod,
info: SpaceChildInfo, currentDepth: Int, maxDepth: Int) {
+ val host = this
if (currentDepth >= maxDepth) return
val childSummary = summaries?.firstOrNull { it.roomId == info.childRoomId } ?: return
// does it have children?
@@ -199,21 +202,21 @@ class SpaceSummaryController @Inject constructor(
val isSelected = selected is RoomGroupingMethod.BySpace && childSummary.roomId == selected.space()?.roomId
subSpaceSummaryItem {
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
id(childSummary.roomId)
hasChildren(!subSpaces.isNullOrEmpty())
selected(isSelected)
expanded(expanded)
- onMore { callback?.onSpaceSettings(childSummary) }
+ onMore { host.callback?.onSpaceSettings(childSummary) }
matrixItem(childSummary.toMatrixItem())
- listener { callback?.onSpaceSelected(childSummary) }
- toggleExpand { callback?.onToggleExpand(childSummary) }
+ listener { host.callback?.onSpaceSelected(childSummary) }
+ toggleExpand { host.callback?.onToggleExpand(childSummary) }
indent(currentDepth)
countState(
UnreadCounterBadgeView.State(
childSummary.notificationCount,
childSummary.highlightCount > 0,
- childSummary.scUnreadCount(scSdkPreferences),
+ childSummary.scUnreadCount(host.scSdkPreferences),
childSummary.markedUnread
)
)
diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt
index 8a87ff3473..a8b85c9887 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt
@@ -36,23 +36,24 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
// var shouldForceFocusOnce = true
override fun buildModels(data: CreateSpaceState?) {
+ val host = this
genericFooterItem {
id("info_help_header")
style(ItemStyle.TITLE)
text(
if (data?.spaceType == SpaceType.Public) {
- stringProvider.getString(R.string.create_spaces_room_public_header, data.name)
+ host.stringProvider.getString(R.string.create_spaces_room_public_header, data.name)
} else {
- stringProvider.getString(R.string.create_spaces_room_private_header)
+ host.stringProvider.getString(R.string.create_spaces_room_private_header)
}
)
- textColor(colorProvider.getColorFromAttribute(R.attr.riot_primary_text_color))
+ textColor(host.colorProvider.getColorFromAttribute(R.attr.riot_primary_text_color))
}
genericFooterItem {
id("info_help")
text(
- stringProvider.getString(
+ host.stringProvider.getString(
if (data?.spaceType == SpaceType.Public) {
R.string.create_spaces_room_public_header_desc
} else {
@@ -60,7 +61,7 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
}
)
)
- textColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary))
+ textColor(host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary))
}
val firstRoomName = data?.defaultRooms?.get(0)
@@ -69,11 +70,11 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
enabled(true)
value(firstRoomName)
singleLine(true)
- hint(stringProvider.getString(R.string.create_room_name_section))
+ hint(host.stringProvider.getString(R.string.create_room_name_section))
endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT)
showBottomSeparator(false)
onTextChange { text ->
- listener?.onNameChange(0, text)
+ host.listener?.onNameChange(0, text)
}
}
@@ -83,11 +84,11 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
enabled(true)
value(secondRoomName)
singleLine(true)
- hint(stringProvider.getString(R.string.create_room_name_section))
+ hint(host.stringProvider.getString(R.string.create_room_name_section))
endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT)
showBottomSeparator(false)
onTextChange { text ->
- listener?.onNameChange(1, text)
+ host.listener?.onNameChange(1, text)
}
}
@@ -97,11 +98,11 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
enabled(true)
value(thirdRoomName)
singleLine(true)
- hint(stringProvider.getString(R.string.create_room_name_section))
+ hint(host.stringProvider.getString(R.string.create_room_name_section))
endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT)
showBottomSeparator(false)
onTextChange { text ->
- listener?.onNameChange(2, text)
+ host.listener?.onNameChange(2, text)
}
// onBind { _, view, _ ->
// if (shouldForceFocusOnce
diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt
index d46ae36275..6ab35d3bf6 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt
@@ -37,13 +37,14 @@ class SpaceDetailEpoxyController @Inject constructor(
// var shouldForceFocusOnce = true
override fun buildModels(data: CreateSpaceState?) {
+ val host = this
genericFooterItem {
id("info_help")
text(
if (data?.spaceType == SpaceType.Public) {
- stringProvider.getString(R.string.create_spaces_details_public_header)
+ host.stringProvider.getString(R.string.create_spaces_details_public_header)
} else {
- stringProvider.getString(R.string.create_spaces_details_private_header)
+ host.stringProvider.getString(R.string.create_spaces_details_private_header)
}
)
}
@@ -52,17 +53,17 @@ class SpaceDetailEpoxyController @Inject constructor(
id("avatar")
enabled(true)
imageUri(data?.avatarUri)
- avatarRenderer(avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
matrixItem(data?.name?.let { MatrixItem.RoomItem("!", it, null).takeIf { !it.displayName.isNullOrBlank() } })
- clickListener { listener?.onAvatarChange() }
- deleteListener { listener?.onAvatarDelete() }
+ clickListener { host.listener?.onAvatarChange() }
+ deleteListener { host.listener?.onAvatarDelete() }
}
formEditTextItem {
id("name")
enabled(true)
value(data?.name)
- hint(stringProvider.getString(R.string.create_room_name_hint))
+ hint(host.stringProvider.getString(R.string.create_room_name_hint))
singleLine(true)
showBottomSeparator(false)
errorMessage(data?.nameInlineError)
@@ -76,7 +77,7 @@ class SpaceDetailEpoxyController @Inject constructor(
// }
// }
onTextChange { text ->
- listener?.onNameChange(text)
+ host.listener?.onNameChange(text)
}
}
@@ -84,11 +85,11 @@ class SpaceDetailEpoxyController @Inject constructor(
id("topic")
enabled(true)
value(data?.topic)
- hint(stringProvider.getString(R.string.create_space_topic_hint))
+ hint(host.stringProvider.getString(R.string.create_space_topic_hint))
showBottomSeparator(false)
textSizeSp(16)
onTextChange { text ->
- listener?.onTopicChange(text)
+ host.listener?.onTopicChange(text)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt
index 28d410529e..e334868d7c 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt
@@ -26,7 +26,8 @@ import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
-import im.vector.app.core.ui.list.genericFooterItem
+import im.vector.app.core.ui.list.GenericEmptyWithActionItem
+import im.vector.app.core.ui.list.genericEmptyWithActionItem
import im.vector.app.core.ui.list.genericPillItem
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.list.spaceChildInfoItem
@@ -35,7 +36,7 @@ import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError.Companion.M_UNRECOGNIZED
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
-import org.matrix.android.sdk.api.util.MatrixItem
+import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
class SpaceDirectoryController @Inject constructor(
@@ -50,11 +51,13 @@ class SpaceDirectoryController @Inject constructor(
fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo)
fun onRoomClick(spaceChildInfo: SpaceChildInfo)
fun retry()
+ fun addExistingRooms(spaceId: String)
}
var listener: InteractionListener? = null
override fun buildModels(data: SpaceDirectoryState?) {
+ val host = this
val results = data?.spaceSummaryApiResult
if (results is Incomplete) {
@@ -70,13 +73,13 @@ class SpaceDirectoryController @Inject constructor(
tintIcon(false)
text(
span {
- span(stringProvider.getString(R.string.spaces_no_server_support_title)) {
+ span(host.stringProvider.getString(R.string.spaces_no_server_support_title)) {
textStyle = "bold"
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)
}
+"\n\n"
- span(stringProvider.getString(R.string.spaces_no_server_support_description)) {
- textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
+ span(host.stringProvider.getString(R.string.spaces_no_server_support_description)) {
+ textColor = host.colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
}
}
)
@@ -84,8 +87,8 @@ class SpaceDirectoryController @Inject constructor(
} else {
errorWithRetryItem {
id("api_err")
- text(errorFormatter.toHumanReadable(failure))
- listener { listener?.retry() }
+ text(host.errorFormatter.toHumanReadable(failure))
+ listener { host.listener?.retry() }
}
}
} else {
@@ -96,9 +99,23 @@ class SpaceDirectoryController @Inject constructor(
?: emptyList()
if (flattenChildInfo.isEmpty()) {
- genericFooterItem {
- id("empty_footer")
- stringProvider.getString(R.string.no_result_placeholder)
+ genericEmptyWithActionItem {
+ id("empty_res")
+ title(host.stringProvider.getString(R.string.this_space_has_no_rooms))
+ iconRes(R.drawable.ic_empty_icon_room)
+ iconTint(host.colorProvider.getColorFromAttribute(R.attr.riotx_reaction_background_on))
+ apply {
+ if (data?.canAddRooms == true) {
+ description(host.stringProvider.getString(R.string.this_space_has_no_rooms_admin))
+ val action = GenericEmptyWithActionItem.Action(host.stringProvider.getString(R.string.space_add_existing_rooms))
+ action.perform = Runnable {
+ host.listener?.addExistingRooms(data.spaceId)
+ }
+ buttonAction(action)
+ } else {
+ description(host.stringProvider.getString(R.string.this_space_has_no_rooms_not_admin))
+ }
+ }
}
} else {
flattenChildInfo.forEach { info ->
@@ -107,24 +124,24 @@ class SpaceDirectoryController @Inject constructor(
val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
spaceChildInfoItem {
id(info.childRoomId)
- matrixItem(MatrixItem.RoomItem(info.childRoomId, info.name, info.avatarUrl))
- avatarRenderer(avatarRenderer)
+ matrixItem(info.toMatrixItem())
+ avatarRenderer(host.avatarRenderer)
topic(info.topic)
memberCount(info.activeMemberCount ?: 0)
space(isSpace)
loading(isLoading)
buttonLabel(
- if (isJoined) stringProvider.getString(R.string.action_open)
- else stringProvider.getString(R.string.join)
+ if (isJoined) host.stringProvider.getString(R.string.action_open)
+ else host.stringProvider.getString(R.string.join)
)
apply {
if (isSpace) {
- itemClickListener(View.OnClickListener { listener?.onSpaceChildClick(info) })
+ itemClickListener(View.OnClickListener { host.listener?.onSpaceChildClick(info) })
} else {
- itemClickListener(View.OnClickListener { listener?.onRoomClick(info) })
+ itemClickListener(View.OnClickListener { host.listener?.onRoomClick(info) })
}
}
- buttonClickListener(View.OnClickListener { listener?.onButtonClick(info) })
+ buttonClickListener(View.OnClickListener { host.listener?.onButtonClick(info) })
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt
index fa44f4595e..a866ea9b89 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt
@@ -19,6 +19,8 @@ package im.vector.app.features.spaces.explore
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.activityViewModel
@@ -26,9 +28,12 @@ import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
+import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentRoomDirectoryPickerBinding
+import im.vector.app.features.spaces.manage.ManageType
+import im.vector.app.features.spaces.manage.SpaceManageActivity
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import javax.inject.Inject
@@ -44,6 +49,8 @@ class SpaceDirectoryFragment @Inject constructor(
SpaceDirectoryController.InteractionListener,
OnBackPressed {
+ override fun getMenuRes() = R.menu.menu_space_directory
+
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentRoomDirectoryPickerBinding.inflate(layoutInflater, container, false)
@@ -60,6 +67,10 @@ class SpaceDirectoryFragment @Inject constructor(
}
epoxyController.listener = this
views.roomDirectoryPickerList.configureWith(epoxyController)
+
+ viewModel.selectSubscribe(this, SpaceDirectoryState::canAddRooms) {
+ invalidateOptionsMenu()
+ }
}
override fun onDestroyView() {
@@ -77,6 +88,28 @@ class SpaceDirectoryFragment @Inject constructor(
views.toolbar.title = title
}
+ override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
+ menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
+ menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented
+ super.onPrepareOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.spaceAddRoom -> {
+ withState(viewModel) { state ->
+ addExistingRooms(state.spaceId)
+ }
+ return true
+ }
+ R.id.spaceCreateRoom -> {
+ // not implemented yet
+ return true
+ }
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
override fun onButtonClick(spaceChildInfo: SpaceChildInfo) {
viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo))
}
@@ -97,6 +130,14 @@ class SpaceDirectoryFragment @Inject constructor(
override fun retry() {
viewModel.handle(SpaceDirectoryViewAction.Retry)
}
+
+ private val addExistingRoomActivityResult = registerStartForActivityResult { _ ->
+ viewModel.handle(SpaceDirectoryViewAction.Retry)
+ }
+
+ override fun addExistingRooms(spaceId: String) {
+ addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms))
+ }
// override fun navigateToRoom(roomId: String) {
// viewModel.handle(SpaceDirectoryViewAction.NavigateToRoom(roomId))
// }
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt
index 75adc659d5..220c3e3492 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt
@@ -36,7 +36,8 @@ data class SpaceDirectoryState(
// Set of joined roomId / spaces,
val joinedRoomsIds: Set = emptySet(),
// keys are room alias or roomId
- val changeMembershipStates: Map = emptyMap()
+ val changeMembershipStates: Map = emptyMap(),
+ val canAddRooms: Boolean = false
) : MvRxState {
constructor(args: SpaceDirectoryArgs) : this(
spaceId = args.spaceId
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt
index 0c23752936..313ddfe1dc 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt
@@ -28,12 +28,15 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
+import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
@@ -70,6 +73,23 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
refreshFromApi()
observeJoinedRooms()
observeMembershipChanges()
+ observePermissions()
+ }
+
+ private fun observePermissions() {
+ val room = session.getRoom(initialState.spaceId) ?: return
+
+ val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable()
+
+ powerLevelsContentLive
+ .subscribe {
+ val powerLevelsHelper = PowerLevelsHelper(it)
+ setState {
+ copy(canAddRooms = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
+ EventType.STATE_SPACE_CHILD))
+ }
+ }
+ .disposeOnClear()
}
private fun refreshFromApi() {
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt
index dffb09529b..69be246506 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt
@@ -62,6 +62,22 @@ class AddRoomListController @Inject constructor(
var initialLoadOccurred = false
+ var expanded: Boolean = true
+ set(value) {
+ if (value != field) {
+ field = value
+ requestForcedModelBuild()
+ }
+ }
+
+ var disabled: Boolean = false
+ set(value) {
+ if (value != field) {
+ field = value
+ requestForcedModelBuild()
+ }
+ }
+
fun boundaryChange(boundary: ResultBoundaries) {
val boundaryHasLoadedSomething = boundary.frontLoaded || boundary.zeroItemLoaded
if (initialLoadOccurred != boundaryHasLoadedSomething) {
@@ -88,6 +104,11 @@ class AddRoomListController @Inject constructor(
}
override fun addModels(models: List>) {
+ if (disabled) {
+ super.addModels(emptyList())
+ return
+ }
+ val host = this
val filteredModel = if (ignoreRooms == null) {
models
} else {
@@ -100,38 +121,44 @@ class AddRoomListController @Inject constructor(
add(
RoomCategoryItem_().apply {
id("header")
- title(sectionName ?: "")
- expanded(true)
+ title(host.sectionName ?: "")
+ expanded(host.expanded)
+ listener {
+ host.expanded = !host.expanded
+ }
}
)
- if (subHeaderText != null) {
+ if (expanded && subHeaderText != null) {
add(
GenericPillItem_().apply {
id("sub_header")
- text(subHeaderText)
+ text(host.subHeaderText)
imageRes(R.drawable.ic_info)
}
)
}
}
- super.addModels(filteredModel)
- if (!initialLoadOccurred) {
- add(
- RoomSelectionPlaceHolderItem_().apply { id("loading") }
- )
+ if (expanded) {
+ super.addModels(filteredModel)
+ if (!initialLoadOccurred) {
+ add(
+ RoomSelectionPlaceHolderItem_().apply { id("loading") }
+ )
+ }
}
}
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
+ val host = this
if (item == null) return RoomSelectionPlaceHolderItem_().apply { id(currentPosition) }
return RoomSelectionItem_().apply {
id(item.roomId)
matrixItem(item.toMatrixItem())
- avatarRenderer(this@AddRoomListController.avatarRenderer)
+ avatarRenderer(host.avatarRenderer)
space(item.roomType == RoomType.SPACE)
- selected(selectedItems[item.roomId] ?: false)
+ selected(host.selectedItems[item.roomId] ?: false)
itemClickListener(DebouncedClickListener({
- listener?.onItemSelected(item)
+ host.listener?.onItemSelected(item)
}))
}
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt
index 203098d32b..05a2f19941 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt
@@ -42,6 +42,7 @@ import javax.inject.Inject
class SpaceAddRoomFragment @Inject constructor(
private val spaceEpoxyController: AddRoomListController,
private val roomEpoxyController: AddRoomListController,
+ private val dmEpoxyController: AddRoomListController,
private val viewModelFactory: SpaceAddRoomsViewModel.Factory
) : VectorBaseFragment(),
OnBackPressed, AddRoomListController.Listener, SpaceAddRoomsViewModel.Factory {
@@ -84,6 +85,7 @@ class SpaceAddRoomFragment @Inject constructor(
viewModel.selectionListLiveData.observe(viewLifecycleOwner) {
spaceEpoxyController.selectedItems = it
roomEpoxyController.selectedItems = it
+ dmEpoxyController.selectedItems = it
saveNeeded = it.values.any { it }
invalidateOptionsMenu()
}
@@ -95,6 +97,7 @@ class SpaceAddRoomFragment @Inject constructor(
viewModel.selectSubscribe(this, SpaceAddRoomsState::ignoreRooms) {
spaceEpoxyController.ignoreRooms = it
roomEpoxyController.ignoreRooms = it
+ dmEpoxyController.ignoreRooms = it
}.disposeOnDestroyView()
viewModel.selectSubscribe(this, SpaceAddRoomsState::isSaving) {
@@ -105,6 +108,10 @@ class SpaceAddRoomFragment @Inject constructor(
}
}.disposeOnDestroyView()
+ viewModel.selectSubscribe(this, SpaceAddRoomsState::shouldShowDMs) {
+ dmEpoxyController.disabled = !it
+ }.disposeOnDestroyView()
+
views.createNewRoom.debouncedClicks {
sharedViewModel.handle(SpaceManagedSharedAction.CreateRoom)
}
@@ -121,11 +128,11 @@ class SpaceAddRoomFragment @Inject constructor(
.setNegativeButton(R.string.cancel, null)
.show()
}
- is SpaceAddRoomsViewEvents.SaveFailed -> {
+ is SpaceAddRoomsViewEvents.SaveFailed -> {
showErrorInSnackbar(it.reason)
invalidateOptionsMenu()
}
- SpaceAddRoomsViewEvents.SavedDone -> {
+ SpaceAddRoomsViewEvents.SavedDone -> {
sharedViewModel.handle(SpaceManagedSharedAction.HandleBack)
}
}
@@ -149,6 +156,7 @@ class SpaceAddRoomFragment @Inject constructor(
views.roomList.cleanup()
spaceEpoxyController.listener = null
roomEpoxyController.listener = null
+ dmEpoxyController.listener = null
super.onDestroyView()
}
@@ -181,6 +189,19 @@ class SpaceAddRoomFragment @Inject constructor(
concatAdapter.addAdapter(roomEpoxyController.adapter)
concatAdapter.addAdapter(spaceEpoxyController.adapter)
+ // This controller can be disabled depending on the space type (public or not)
+ viewModel.updatableDMLivePageResult.liveBoundaries.observe(viewLifecycleOwner) {
+ dmEpoxyController.boundaryChange(it)
+ }
+ viewModel.updatableDMLivePageResult.livePagedList.observe(viewLifecycleOwner) {
+ dmEpoxyController.totalSize = it.size
+ dmEpoxyController.submitList(it)
+ }
+ dmEpoxyController.sectionName = getString(R.string.direct_chats_header)
+ dmEpoxyController.listener = this
+
+ concatAdapter.addAdapter(dmEpoxyController.adapter)
+
views.roomList.adapter = concatAdapter
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt
index 6ce3468fe1..2d9113ae68 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt
@@ -26,7 +26,8 @@ data class SpaceAddRoomsState(
val currentFilter: String = "",
val spaceName: String = "",
val ignoreRooms: List = emptyList(),
- val isSaving: Async> = Uninitialized
+ val isSaving: Async> = Uninitialized,
+ val shouldShowDMs : Boolean = false
// val selectionList: Map = emptyMap()
) : MvRxState {
constructor(args: SpaceManageArgs) : this(
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt
index 55e65eb171..35c415b087 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt
@@ -98,6 +98,26 @@ class SpaceAddRoomsViewModel @AssistedInject constructor(
)
}
+ val updatableDMLivePageResult: UpdatableLivePageResult by lazy {
+ session.getFilteredPagedRoomSummariesLive(
+ roomSummaryQueryParams {
+ this.memberships = listOf(Membership.JOIN)
+ this.excludeType = listOf(RoomType.SPACE)
+ this.includeType = null
+ this.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
+ this.activeSpaceFilter = ActiveSpaceFilter.ExcludeSpace(initialState.spaceId)
+ this.displayName = QueryStringValue.Contains(initialState.currentFilter, QueryStringValue.Case.INSENSITIVE)
+ },
+ pagedListConfig = PagedList.Config.Builder()
+ .setPageSize(10)
+ .setInitialLoadSizeHint(20)
+ .setEnablePlaceholders(true)
+ .setPrefetchDistance(10)
+ .build(),
+ sortOrder = RoomSortOrder.NAME
+ )
+ }
+
private val selectionList = mutableMapOf()
val selectionListLiveData = MutableLiveData
Gera notandaaðgang óvirkann
Gera notandaaðganginn minn óvirkann
-
Senda greiningargögn
- Já, ég vil hjálpa til
-
+ Já, ég vil hjálpa til!
Yfirfara núna
-
Gera notandaaðgang óvirkann
Til að halda áfram, settu inn lykilorðið þitt:
Gera notandaaðgang óvirkann
-
- "Símafundur í gangi.\nTaka þátt með %1$s eða %2$s."
+ Símafundur í gangi.
+\nTaka þátt með %1$s eða %2$s
hljóð-
myndsímtali
Þú þarft aðgangsheimildir til að bjóða til símafundar á þessari spjallrás
@@ -854,31 +732,25 @@ Leyfa ${app_name} nota tengiliðina ?
Forritið hrundi síðast. Myndirðu vilja senda inn villuskýrslu?
Senda límmerki
Tölvupósttengill sem ekki er enn búið að smella á
-
Rangt formað auðkenni. Ætti að vera tölvupóstfang eða Matrix-auðkenni á borð við\'@sérheiti:lén\'
${app_name} safnar nafnlausum greiningargögnum til að gera okkur kleift að bæta forritið.
Endilega virkjaðu greiningargögn til að hjálpa okkur að bæta ${app_name}.
Til að tengja við spjallrás verður hún að vera með vistfang.
Þú ert að reyna að tengjast %s. Myndirðu vilja gerast meðlimur til að geta tekið þátt í samræðunni?
Þetta er forskoðun á spjallrásinni. Samskipti spjallrásarinnar hafa verið gerð óvirk.
-
Heimaskjár
Festa spjallrásir með óskoðuðum tilkynningum
Festa spjallrásir með ólesnum skilaboðum
Sjálfgefið virkja forskoðun innfelldra vefslóða
Hver sá sem þekkir slóðina á spjallrásina, fyrir utan gesti
Hver sá sem þekkir slóðina á spjallrásina, að gestum meðtöldum
-
Þetta eru eiginleikar á tilraunastigi sem gætu bilað á óvæntan hátt. Notist með varúð.
Þú þarft að skrá þig út til að geta virkjað dulritunina.
Aðeins dulrita til sannvottaðra tækja
Aldrei senda dulrituð skilaboð af þessu tæki til ósannvottaðra tækja.
-
Aðvaranir vegna aðalvistfangs
-
Setja sem aðalvistfang
Ekki setja sem aðalvistfang
Nauðsynlegt gildi vantar.
Gildið er ekki gilt.
-
-
+
\ No newline at end of file
diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml
index 542ab5783b..520fac1900 100644
--- a/vector/src/main/res/values-it/strings.xml
+++ b/vector/src/main/res/values-it/strings.xml
@@ -2814,4 +2814,23 @@
Spazi
Inviti
Stanze suggerite
+ Gestisci stanze e spazi
+ Segna come non consigliato
+ Segna come consigliato
+ Consigliato
+ Rendi questo spazio pubblico
+ Gestisci stanze
+ Cerchi qualcuno non in %s\?
+ %s ti ha invitato
+ Questa stanza è pubblica
+ Invia i file multimediali nella dimensione originale
+
+ - Invia il video nella dimensione originale
+ - Invia i video nella dimensione originale
+
+ Il file è troppo grande per essere inviato.
+ Compressione video %d%%
+ Compressione immagine…
+ Usa come predefinito e non chiedere più
+ Chiedi sempre
\ No newline at end of file
diff --git a/vector/src/main/res/values-lt/strings.xml b/vector/src/main/res/values-lt/strings.xml
index 6eb730da63..b18595388c 100644
--- a/vector/src/main/res/values-lt/strings.xml
+++ b/vector/src/main/res/values-lt/strings.xml
@@ -32,4 +32,80 @@
%1$s išėjo iš kambario
Jūs prisijungėte
%1$s prisijungė
+ Pranešimai
+ Versija
+ Normalūs
+ Įjungti
+ Įjungti
+ Tel. numeris
+ E. paštas
+ Versija
+ Nustatymai
+ Nustatymai
+ Atsijungti
+ Ieškoti
+ Priežastis
+ Ignoruoti
+ Atblokuoti
+ Užblokuoti
+ Pakviesti
+ Prisijungę
+ Atsijungę
+ Sukurti
+ Sinchronizuojama…
+ Atmesti
+ Peržiūra
+ Prisijungti
+ Pašalinti
+ Tęsti
+ NE
+ TAIP
+ Informacija
+ Skambinama…
+ Skambutis
+ Skambučiai
+ Šiandien
+ Jūs pakvietėte %1$s
+ %1$s pakvietė %2$s
+ Jūs išsiuntėte pakvietimą %1$s prisijungti prie kambario
+ Jūs atnaujinote savo profilį %1$s
+ %1$s atnaujino savo profilį %2$s
+ Žinutę pašalino %1$s [Priežastis: %2$s]
+ Žinutė pašalinta [Priežastis: %1$s]
+ Žinutę pašalino %1$s
+ Žinutė pašalinta
+ Jūs pašalinote kambario nuotrauką
+ %1$s pašalino kambario nuotrauka
+ Jūs pašalinote kambario tema
+ %1$s pašalino kambario temą
+ Jūs pašalinote kambario pavadinimą
+ %1$s pašalino kambario pavadinimą
+ VoIP konferencija baigta
+ VoIP konferencija pradėta
+ Jūs paprašėte VoIP konferencijos
+ 🎉 Visiem serveriam yra uždrausta dalyvauti! Šiuo kambariu nebegalima naudotis.
+ Jūs atnaujinote šį kambarį.
+ %s atnaujino šį kambarį.
+ Jūs įjungėte ištisinį šifravimą (%1$s)
+ %1$s įjungė ištisinį šifravimą (%2$s)
+ nežinomas (%s).
+ bet kas.
+ Visi kambario dalyviai.
+ Visi kambario dalyviai, nuo tada, kai jie yra pakviesti.
+ %s baigė skambutį.
+ Jūs atsiliepėte į skambutį.
+ %s atsiliepė į skambutį.
+ %s pradėjo balso skambutį.
+ Jūs pradėjote vaizdo skambutį.
+ %s pradėjo vaizdo skambutį.
+ %1$s pakeitė kambario pavadinimą į %2$s
+ Jūs pakeitėte kambario nuotrauka
+ Jūs pakeitėte temą į %1$s
+ %1$s pakeitė temą į: %2$s
+ Jūs pakeitėte savo vardą iš %1$s į %2$s
+ %1$s pakeitė savo vardą iš %2$s į %3$s
+ Jūs pakeitėte savo vardą į %1$s
+ %1$s pakeitė savo vardą į %2$s
+ Jūs pakeitėte savo profilio nuotrauką
+ %1$s pakeitė savo profilio nuotrauką
\ No newline at end of file
diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml
index 72c7faa1ca..902c0df49e 100644
--- a/vector/src/main/res/values-pl/strings.xml
+++ b/vector/src/main/res/values-pl/strings.xml
@@ -2,7 +2,6 @@
%1$s: %2$s
%1$s wysłał(a) zdjęcie.
-
Zaproszenie od %s
%1$s zaprosił(a) %2$s
%1$s zaprosił(a) Cię
@@ -18,15 +17,11 @@
%1$s usunął(-ęła) swoją wyświetlaną nazwę (%2$s)
%1$s zmienił(a) temat na: %2$s
Nie można wysłać wiadomości
-
Przesyłanie zdjęcia nie powiodło się
-
Błąd sieci
Błąd Matrixa
-
Adres e-mail
Numer telefonu
-
wszyscy członkowie pokoju.
wszyscy.
%1$s zmienił(a) nazwę pokoju na: %2$s
@@ -34,25 +29,20 @@
%1$s usunął(-ęła) nazwę pokoju
%1$s usunął(-ęła) temat pokoju
%1$s wysłał(a) naklejkę.
-
%1$s włączył(a) szyfrowanie end-to-end (%2$s)
-
%1$s wycofał(a) zaproszenie %2$s
%s odebrał(a) połączenie.
(awatar też został zmieniony)
-
Zaproszenie od %s
Zaproszenie do pokoju
%1$s i %2$s
Pusty pokój
-
- %1$s i jeden inny
- %1$s i kilku innych
- %1$s i %2$d innych
-
+
-
** Nie można odszyfrować: %s **
%s wykonał(a) rozmowę wideo.
%s wykonał(a) połączenie głosowe.
@@ -63,22 +53,17 @@
%1$s zażądał(a) grupowego połączenia VoIP
Rozpoczęto grupowe połączenie głosowe VoIP
Zakończono grupowe połączenie głosowe VoIP
-
%1$s zaktualizował swój profil %2$s
%1$s wysłał(a) zaproszenie do %2$s aby dołączył(a) do tego pokoju
%1$s zaakceptował(a) zaproszenie dla %2$s
-
Urządzenie nadawcy nie wysłało nam kluczy do tej wiadomości.
-
Nie można zredagować
Obecnie nie jest możliwe ponowne dołączenie do pustego pokoju.
-
Wiadomość usunięta
Wiadomość usunięta przez %1$s
Wiadomość usunięta [powód: %1$s]
Wiadomość usunięta przez %1$s [powód: %2$s]
%s zakutalizował(a) ten pokój.
-
Synchronizacja początkowa:
\nImportowanie konta…
Synchronizacja początkowa:
@@ -95,7 +80,6 @@
\nImportowanie Społeczności
Synchronizacja początkowa:
\nImportowanie danych Konta
-
Wysyłanie wiadomości…
Wyczyść kolejkę wysyłania
Wiadomości
@@ -890,8 +874,8 @@ Widoczność wiadomości w Matrix jest podobna do wiadomości e-mail. Nasze zapo
- %d godzina
- %d godz.
-
-
+
+
- %d dzień
@@ -2403,4 +2387,48 @@ Spróbuj uruchomić ponownie aplikację.
\nCzy na pewno kontynuować\?
Kliknij dwukrotnie w ten link
Wybierz hasło.
+ • Dopasowanie serwera %s jest zbanowane.
+ • Dopasowanie serwera %s jest dozwolone.
+ Ustawiłeś serwer ACL dla tego pokoju.
+ %s ustawił(a) serwer ACL dla tego pokoju.
+ Uaktualniłeś tutaj.
+ %s uaktualnił(a) tutaj.
+ Uaktualniłeś ten pokój.
+ Włączyłeś szyfrowanie end-to-end (%1$s)
+ Ustawiłeś przyszłe wiadomości widoczne dla %1$s
+ %1$s ustawił przyszłe wiadomości widoczne dla %2$s
+ Udostępniłeś przyszłą historię pokoju dla %1$s
+ Zakończyłeś rozmowę.
+ Odebrałeś rozmowę.
+ Wysłałeś dane, aby nawiązać połączenie.
+ %s wysłał(a) dane, aby nawiązać połączenie.
+ Nawiązałeś połączenie głosowe.
+ Nawiązałeś rozmowę wideo.
+ Zmieniłeś nazwę pokoju na: %1$s
+ Zmieniłeś awatar pokoju
+ %1$s zmienił(a) awatar pokoju
+ Zmieniłeś temat na %1$s
+ Usunąłeś wyświetlaną nazwę (Była %1$s)
+ Zmieniłeś wyświetlaną nazwę z %1$s na %2$s
+ Ustawiłeś wyświetlaną nazwę na %1$s
+ Zmieniłeś swój awatar
+ Wycofałeś zaproszenie %1$s
+ Zbanowałeś %1$s
+ Odbanowałeś %1$s
+ Wyrzuciłeś %1$s
+ Odrzuciłeś zaproszenie
+ Opuściłeś pokój
+ %1$s opuścił(a) pokój
+ Opuściłeś pokój
+ Dołączyłeś
+ %1$s dołączył
+ Dołączyłeś do pokoju
+ Zaprosiłeś %1$s
+ Utworzyłeś dyskusję
+ %1$s utworzył(a) dyskusję
+ Utworzyłeś pokój
+ %1$s utworzył(a) pokój
+ Twoje zaproszenie
+ Wysłałeś naklejkę.
+ Wysłałeś zdjęcie.
\ No newline at end of file
diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml
index b46bacebca..f55453707f 100644
--- a/vector/src/main/res/values-pt-rBR/strings.xml
+++ b/vector/src/main/res/values-pt-rBR/strings.xml
@@ -1,224 +1,224 @@
%1$s: %2$s
- %1$s enviou uma foto.
+ %1$s enviou uma imagem.
convite de %s
%1$s convidou %2$s
%1$s convidou você
- %1$s entrou na sala
+ %1$s juntou-se à sala
%1$s saiu da sala
- %1$s recusou o convite
- %1$s removeu %2$s
- %1$s removeu o banimento de %2$s
+ %1$s rejeitou o convite
+ %1$s expulsou %2$s
+ %1$s desbaniu %2$s
%1$s baniu %2$s
- %1$s desfez o convite a %2$s
- %1$s alterou a foto de perfil
- %1$s definiu o nome e sobrenome como %2$s
- %1$s alterou o nome e sobrenome de %2$s para %3$s
- %1$s removeu o nome e sobrenome (era %2$s)
- %1$s alterou a descrição para: %2$s
- %1$s alterou o nome da sala para: %2$s
- %s iniciou uma chamada de vídeo.
- %s iniciou uma chamada de voz.
- %s aceitou a chamada.
- %s encerrou a chamada.
+ %1$s retirou o convite de %2$s
+ %1$s mudou seu avatar
+ %1$s definiu seu nome de exibição para %2$s
+ %1$s mudou seu nome de exibição de %2$s para %3$s
+ %1$s removeu seu nome de exibição (era %2$s)
+ %1$s mudou o tópico para: %2$s
+ %1$s mudou o nome da sala para: %2$s
+ %s começou uma chamada de vídeo.
+ %s começou uma chamada de voz.
+ %s atendeu a chamada.
+ %s terminou a chamada.
%1$s deixou o histórico futuro da sala visível para %2$s
- todos os participantes da sala, a partir do momento em que foram convidados.
- todos os participantes da sala, a partir do momento em que entraram nela.
- todos os participantes da sala.
+ todos os membros da sala, do ponto que foram convidados.
+ todos os mebros da sala, do ponto que se juntaram.
+ todos os membros da sala.
qualquer pessoa.
- desconhecido (%s).
- %1$s ativou a criptografia de ponta a ponta (%2$s)
- %1$s deseja iniciar uma chamada em grupo
- Chamada em grupo iniciada
- Chamada em grupo encerrada
- (a foto de perfil também foi alterada)
+ desconhecida (%s).
+ %1$s ativou a encriptação ponta-a-ponta (%2$s)
+ %1$s requisitou uma conferência de VoIP
+ Conferência de VoIP começou
+ Conferência de VoIP terminou
+ (avatar mudou também)
%1$s removeu o nome da sala
- %1$s removeu a descrição da sala
- %1$s atualizou o perfil %2$s
- %1$s enviou um convite para %2$s entrar na sala
+ %1$s removeu o tópico da sala
+ %1$s atualizou seu perfil %2$s
+ %1$s enviou um convite para %2$s para se juntar à sala
%1$s aceitou o convite para %2$s
- ** Não foi possível descriptografar: %s **
- O aparelho do remetente não nos enviou as chaves para esta mensagem.
+ ** Incapaz de decriptar: %s **
+ O dispositivo do(a) enviador(a) não nos enviou as chaves para esta mensagem.
Não foi possível redigir
- Não foi possível enviar a mensagem
- O envio da imagem falhou
+ Não foi possível enviar mensagem
+ Upload de imagem falhou
- Erro de conexão à internet
- Erro no servidor Matrix
+ Erro de rede
+ Erro de Matrix
- Atualmente, não é possível entrar novamente em uma sala vazia.
+ Atualmente não é possível se rejuntar a uma sala vazia.
- Endereço de e-mail
+ Endereço de email
Número de telefone
- %1$s enviou uma figurinha.
+ %1$s enviou um sticker.
Convite de %s
- Convite para sala
+ Convite de Sala
%1$s e %2$s
Sala vazia
- %1$s e 1 outro
- %1$s e %2$d outros
- Você enviou uma foto.
- Você enviou uma figurinha.
+ Você enviou uma imagem.
+ Você enviou um sticker.
Seu convite
%1$s criou a sala
Você criou a sala
Você convidou %1$s
- Você entrou na sala
+ Você juntou-se à sala
Você saiu da sala
- Você recusou o convite
- Você removeu %1$s
- Você removeu o banimento de %1$s
+ Você rejeitou o convite
+ Você expulsou %1$s
+ Você desbaniu %1$s
Você baniu %1$s
- Você desfez o convite a %1$s
- Você alterou a sua foto de perfil
- Você definiu o seu nome e sobrenome como %1$s
- Você alterou o seu nome e sobrenome de %1$s para %2$s
- Você removeu o seu nome e sobrenome (era %1$s)
- Você alterou a descrição para: %1$s
- %1$s alterou a foto da sala
- Você alterou a foto da sala
- Você alterou o nome da sala para: %1$s
- Você iniciou uma chamada de vídeo.
- Você iniciou uma chamada de voz.
+ Você retirou o convite de %1$s
+ Você mudou seu avatar
+ Você definiu seu nome de exibição para %1$s
+ Você mudou seu nome de exibição de %1$s para %2$s
+ Você removeu seu nome de exibição (era %1$s)
+ Você mudou o tópico para: %1$s
+ %1$s mudou o avatar da sala
+ Você mudou o avatar da sala
+ Você mudou o nome da sala para: %1$s
+ Você começou uma chamada de vídeo.
+ Você começou uma chamada de voz.
%s enviou dados para configurar a chamada.
Você enviou dados para configurar a chamada.
- Você aceitou a chamada.
- Você encerrou a chamada.
+ Você atendeu a chamada.
+ Você terminou a chamada.
Você deixou o histórico futuro da sala visível para %1$s
- Você ativou a criptografia de ponta a ponta (%1$s)
- %s atualizou esta sala.
- Você atualizou esta sala.
- Você solicitou uma chamada em grupo
+ Você ativou a encriptação ponta-a-ponta (%1$s)
+ %s fez o upgrade desta sala.
+ Você fez o upgrade desta sala.
+ Você requisitou uma conferência de VoIP
Você removeu o nome da sala
- Você removeu a descrição da sala
- %1$s removeu a foto da sala
- Você removeu a foto da sala
- Mensagem apagada
- Mensagem apagada por %1$s
- Mensagem apagada [motivo: %1$s]
- Mensagem apagada por %1$s [motivo: %2$s]
- Você atualizou o seu perfil %1$s
- Você enviou um convite para %1$s entrar na sala
- %1$s cancelou o convite a %2$s para entrar na sala
- Você cancelou o convite a %1$s para entrar na sala
+ Você removeu o tópico da sala
+ %1$s removeu o avatar da sala
+ Você removeu o avatar da sala
+ Mensagem removida
+ Mensagem removida por %1$s
+ Mensagem removida [razão: %1$s]
+ Mensagem removida por %1$s [razão: %2$s]
+ Você atualizou seu perfil %1$s
+ Você enviou um convite para %1$s para se juntar à sala
+ %1$s revogou o convite para %2$s para entrar na sala
+ Você revogou o convite para %1$s para entrar na sala
Você aceitou o convite para %1$s
- %1$s adicionou o widget %2$s
- Você adicionou o widget %1$s
- %1$s removeu o widget %2$s
- Você removeu o widget %1$s
- %1$s editou o widget %2$s
- Você editou o widget %1$s
- Administrador
- Moderador
- Padrão
+ %1$s adicionou widget %2$s
+ Você adicionou widget %1$s
+ %1$s removeu widget %2$s
+ Você removeu widget %1$s
+ %1$s modificou widget %2$s
+ Você modificou widget %1$s
+ Admin
+ Moderador(a)
+ Default
Personalizado (%1$d)
Personalizado
- Você alterou o nível de permissão de %1$s.
- %1$s alterou o nível de permissão de %2$s.
+ Você mudou o nível de poder de %1$s.
+ %1$s mudou o nível de poder de %2$s.
%1$s de %2$s para %3$s
- Primeira sincronização:
-\nImportando a conta…
- Primeira sincronização:
-\nImportando as chaves de criptografia
- Primeira sincronização:
-\nImportando as salas
- Primeira sincronização:
-\nImportando as salas em que você entrou
- Primeira sincronização:
-\nImportando as salas em que você foi convidado
- Primeira sincronização:
-\nImportando as salas em que você saiu
- Primeira sincronização:
-\nImportando as comunidades
- Primeira sincronização:
-\nImportando os dados da conta
+ Sinc Inicial:
+\nImportando conta…
+ Sinc Inicial:
+\nImportando crypto
+ Sinc Inicial:
+\nImportando Salas
+ Sinc Inicial:
+\nImportando Salas Que Você Se Juntou
+ Sinc Inicial:
+\nImportando Salas Que Você Foi Convidada(o)
+ Sinc Inicial:
+\nImportando Salas de Que Você Saiu
+ Sinc Inicial:
+\nImportando Comunidades
+ Sinc Inicial:
+\nImportando Dados de Conta
Enviando mensagem…
Limpar a fila de envio
- Convite de %1$s. Motivo: %2$s
- O seu convite. Motivo: %1$s
- %1$s convidou %2$s. Motivo: %3$s
- Você convidou %1$s. Motivo: %2$s
- %1$s convidou você. Motivo: %2$s
- %1$s entrou na sala. Motivo: %2$s
- Você entrou na sala. Motivo: %1$s
- %1$s saiu da sala. Motivo: %2$s
- Você saiu da sala. Motivo: %1$s
- %1$s recusou o convite. Motivo: %2$s
- Você recusou o convite. Motivo: %1$s
- %1$s removeu %2$s. Motivo: %3$s
- Você removeu %1$s. Motivo: %2$s
- %1$s removeu o banimento de %2$s. Motivo: %3$s
- Você removeu o banimento de %1$s. Motivo: %2$s
- %1$s baniu %2$s. Motivo: %3$s
- Você baniu %1$s. Motivo: %2$s
- %1$s enviou um convite para %2$s entrar na sala. Motivo: %3$s
- Você enviou um convite para %1$s entrar na sala. Motivo: %2$s
- %1$s revogou o convite para %2$s entrar na sala. Motivo: %3$s
- Você revogou o convite para %1$s entrar na sala. Motivo: %2$s
- %1$s aceitou o convite para %2$s. Motivo: %3$s
- Você aceitou o convite para %1$s. Motivo: %2$s
- %1$s desfez o convite de %2$s. Motivo: %3$s
- Você desfez o convite de %1$s. Motivo: %2$s
+ Convite de %1$s. Razão: %2$s
+ Seu convite. Razão: %1$s
+ %1$s convidou %2$s. Razão: %3$s
+ Você convidou %1$s. Razão: %2$s
+ %1$s convidou você. Razão: %2$s
+ %1$s juntou-se à sala. Razão: %2$s
+ Você juntou-se à sala. Razão: %1$s
+ %1$s saiu da sala. Razão: %2$s
+ Você saiu da sala. Razão: %1$s
+ %1$s rejeitou o convite. Razão: %2$s
+ Você rejeitou o convite. Razão: %1$s
+ %1$s expulsou %2$s. Razão: %3$s
+ Você expulsou %1$s. Razão: %2$s
+ %1$s desbaniu %2$s. Razão: %3$s
+ Você desbaniu %1$s. Razão: %2$s
+ %1$s baniu %2$s. Razão: %3$s
+ Você baniu %1$s. Razão: %2$s
+ %1$s enviou um convite para %2$s para entrar na sala. Razão: %3$s
+ Você enviou um convite para %1$s para entrar na sala. Razão: %2$s
+ %1$s revogou o convite para %2$s para entrar na sala. Razão: %3$s
+ Você revogou o convite para %1$s para entrar na sala. Razão: %2$s
+ %1$s aceitou o convite para %2$s. Razão: %3$s
+ Você aceitou o convite para %1$s. Razão: %2$s
+ %1$s retirou o convite de %2$s. Razão: %3$s
+ Você retirou o convite de %1$s. Razão: %2$s
- - %1$s adicionou %2$s como um endereço desta sala.
- - %1$s adicionou %2$s como endereços desta sala.
+ - %1$s adicionou %2$s como um endereço para esta sala.
+ - %1$s adicionou %2$s como endereços para esta sala.
- - Você adicionou %1$s como um endereço desta sala.
- - Você adicionou %1$s como endereços desta sala.
+ - Você adicionou %1$s como um endereço para esta sala.
+ - Você adicionou %1$s como endereços para esta sala.
- - %1$s removeu %2$s como um endereço desta sala.
- - %1$s removeu %2$s como endereços desta sala.
+ - %1$s removeu %2$s como um endereço para esta sala.
+ - %1$s removeu %2$s como endereços para esta sala.
- - Você removeu %1$s como um endereço desta sala.
- - Você removeu %1$s como endereços desta sala.
+ - Você removeu %1$s como um endereço para esta sala.
+ - Você removeu %1$s como endereços para esta sala.
- %1$s adicionou %2$s e removeu %3$s como endereços desta sala.
- Você adicionou %1$s e removeu %2$s como endereços desta sala.
- %1$s definiu o endereço principal desta sala como %2$s.
- Você definiu o endereço principal desta sala como %1$s.
- %1$s removeu o endereço principal desta sala.
- Você removeu o endereço principal desta sala.
- %1$s permitiu que convidados entrem na sala.
- Você permitiu que convidados entrem na sala.
- %1$s impediu que convidados entrassem na sala.
- Você impediu que convidados entrassem na sala.
- %1$s ativou a criptografia de ponta a ponta.
- Você ativou a criptografia de ponta a ponta.
- %1$s ativou a criptografia de ponta a ponta (algoritmo não reconhecido %2$s).
- Você ativou a criptografia de ponta a ponta (algoritmo não reconhecido %1$s).
- Você impediu que desconhecidos entrem na sala.
- %1$s impediu que desconhecidos entrem na sala.
- Você permitiu que desconhecidos entrem aqui.
- %1$s permitiu que desconhecidos entrem aqui.
- Você saiu. Motivo: %1$s
- %1$s saiu. Motivo: %2$s
- Você entrou. Motivo: %1$s
- %1$s entrou. Motivo: %2$s
- Você cancelou o convite para %1$s
- %1$s cancelou o convite para %2$s
+ %1$s adicionou %2$s e removeu %3$s como endereços para esta sala.
+ Você adicionou %1$s e removeu %2$s como endereços para esta sala.
+ %1$s definiu o endereço principal para esta sala para %2$s.
+ Você definiu o endereço principal para esta sala para %1$s.
+ %1$s removeu o endereço principal para esta sala.
+ Você removeu o endereço principal para esta sala.
+ %1$s tem permitido que visitas se juntem à sala.
+ Você tem permitido que visitas se juntem à sala.
+ %1$s tem prevenido visitas de se juntarem à sala.
+ Você tem prevenido visitas de se juntarem à sala.
+ %1$s ativou encriptação ponta-a-ponta.
+ Você ativou encriptação ponta-a-ponta.
+ %1$s ativou encriptação ponta-a-ponta (algoritmo irreconhecido %2$s).
+ Você ativou encriptação ponta-a-ponta (algoritmo irreconhecido %1$s).
+ Você tem prevenido visitas de se juntarem à sala.
+ %1$s tem prevenido visitas de se juntarem à sala.
+ Você tem permitido que visitas se juntem aqui.
+ %1$s tem permitido que visitas se juntem aqui.
+ Você saiu. Razão: %1$s
+ %1$s saiu. Razão: %2$s
+ Você juntou-se. Razão: %1$s
+ %1$s juntou-se. Razão: %2$s
+ Você revogou o convite para %1$s
+ %1$s revogou o convite para %2$s
Você convidou %1$s
%1$s convidou %2$s
- Você atualizou esta sala.
- %s atualizou esta sala.
- Você definiu que as mensagens enviadas a partir do presente momento estarão disponíveis para %1$s
- %1$s definiu que as mensagens enviadas a partir do presente momento estarão disponíveis para %2$s
+ Você fez o upgrade aqui.
+ %s fez o upgrade aqui.
+ Você deixou mensagens futuras vísiveis para %1$s
+ %1$s deixou mensagens futuras visíveis para %2$s
Você saiu da sala
%1$s saiu da sala
- Você entrou
- %1$s entrou
- Você criou a sala
- %1$s criou a sala
+ Você juntou-se
+ %1$s juntou-se
+ Você criou a discussão
+ %1$s criou a discussão
Sala vazia (era %s)
- %1$s, %2$s, %3$s e %4$d outro
@@ -226,24 +226,24 @@
%1$s, %2$s, %3$s e %4$s
%1$s, %2$s e %3$s
- 🎉 Todos os servidores estão proibidos de participar! Esta sala não pode mais ser usada.
- Nenhuma alteração.
- • Servidores correspondentes aos IP literais agora estão banidos.
- • Servidores correspondentes aos IP literais agora estão permitidos.
- • Servidores correspondentes à %s foram removidos da lista de permitidos.
- • Servidores correspondentes à %s agora são permitidos.
- • Servidores correspondente à %s foram removidos da lista de banidos.
- • Servidores correspondentes à %s foram banidos.
- Você alterou a lista de controle de acesso (ACL) do servidor para esta sala.
- %s alterou a lista de controle de acesso (ACL) do servidor para esta sala.
- • Servidores correspondentes aos IP literais estão banidos.
- • Servidores correspondentes aos IP literais estão permitidos.
- • Servidores correspondentes à %s estão permitidos.
- • Servidores correspondentes à %s estão banidos.
- Você definiu a lista de controle de acesso (ACL) do servidor para esta sala.
- %s definiu a lista de controle de acesso (ACL) do servidor para esta sala.
- Você alterou os endereços alternativos desta sala.
- %1$s alterou os endereços alternativos desta sala.
+ 🎉 Todos os servidores estão banidos de participar! Esta sala não pode mais ser usada.
+ Nenhuma mudança.
+ • Servidores correspondendo a literais de IP estão agora banidos.
+ • Servidores correspondendo a literais de IP estão agora permitidos.
+ • Servidores correspondendo a %s foram removidos da lista de permitidos.
+ • Servidores correspondendo a %s estão agora permitidos.
+ • Servidores correspondendo a %s foram removidos da lista de banimento.
+ • Servidores correspondendo a %s estão agora banidos.
+ Você mudou as LCAs do servidor para esta sala.
+ %s mudou as LCAs do servidor para esta sala.
+ • Servidores correspondendo a literais de IP estão banidos.
+ • Servidores correspondendo a literais de IP estão permitidos.
+ • Servidores correspondendo a %s estão permitidos.
+ • Servidores correspondendo a %s estão banidos.
+ Você definiu as LCAs do servidor para esta sala.
+ %s definiu as LCAs do servidor para esta sala.
+ Você mudou os endereços alternativos para esta sala.
+ %1$s mudou os endereços alternativos para esta sala.
- Você removeu o endereço alternativo %1$s para esta sala.
- Você removeu os endereços alternativos %1$s para esta sala.
@@ -260,74 +260,74 @@
- %1$s adicionou o endereço alternativo %2$s para esta sala.
- %1$s adicionou os endereços alternativos %2$s para esta sala.
- Você alterou os endereços desta sala.
- %1$s alterou os endereços desta sala.
- Você alterou os endereços principal e alternativos desta sala.
- %1$s alterou os endereços principal e alternativos desta sala.
- Você modificou a chamada de vídeo
- Chamada de vídeo modificada por %1$s
- Você encerrou a chamada de vídeo
- Chamada de vídeo encerrada por %1$s
- Você começou uma chamada de vídeo
- Chamada de vídeo iniciada por %1$s
+ Você mudou os endereços para esta sala.
+ %1$s mudou os endereços para esta sala.
+ Você mudou os endereços principal e alternativos para esta sala.
+ %1$s mudou os endereços principal e alternativos para esta sala.
+ Você modificou conferência de vídeo
+ Conferência de vídeo modificada por %1$s
+ Você terminou conferência de vídeo
+ Conferência de vídeo terminada por %1$s
+ Você começou conferência de vídeo
+ Conferência de vídeo começada por %1$s
Mensagens
Sala
Configurações
- Detalhes do participante
+ Detalhes de Membros
Histórico
Aceitar
- Recusar
- Encerrar
+ Declinar
+ Desligar
OK
Cancelar
Salvar
- Sair da sala
+ Sair
Enviar
Reenviar
- Apagar
+ Remover
Citar
Compartilhar
- Depois
+ Mais Tarde
Encaminhar
- Link
- Ver código-fonte
- Ver código-fonte descriptografado
- Apagar
+ Permalink
+ Ver Fonte
+ Ver Fonte Decriptada
+ Deletar
Renomear
- Denunciar conteúdo
- Chamada em andamento
- Chamada em grupo em andamento.
-\nEntre com %1$s ou %2$s
+ Reportar conteúdo
+ Chamada ativa
+ Chamada de conferência em curso.
+\nJunte-se como %1$s ou %2$s
Voz
Vídeo
- Não foi possível iniciar a chamada, tente mais tarde
- Devido à falta de permissões, alguns recursos podem estar faltando…
- Você precisa de permissão para convidar, para iniciar uma chamada em grupo nesta sala
- Não foi possível iniciar a chamada
- Informações da sessão
- Não há suporte para chamadas em grupo em salas criptografadas
- Enviar mesmo assim
+ Não é possível começar a chamada, por favor tente mais tarde
+ Devido a permissões faltando, alguns recursos podem estar faltando…
+ Você precisa de permissão para convidar para começar uma conferência nesta sala
+ Não é possível começar chamada
+ Informação da sessão
+ Chamadas de conferência não são suportadas em salas encriptadas
+ Enviar Mesmo Assim
ou
Convidar
- Sair
- Chamada de voz
- Chamada de vídeo
- Busca global
+ Fazer signout
+ Chamada de Voz
+ Chamada de Vídeo
+ Pesquisa global
Marcar tudo como lido
Histórico
- Responder
+ Responder rápido
Abrir
Fechar
- Copiado para a área de transferência
+ Copiado para clipboard
Desativar
Confirmação
- Atenção
+ Aviso
- Início
+ Home
Favoritos
Pessoas
Salas
@@ -342,112 +342,112 @@
Conversas
Agenda de endereços local
- Apenas contatos na Matrix
+ Contatos de Matrix somente
Nenhuma conversa
- Você não permitiu que o ${app_name} acesse seus contatos locais
+ Você não permitiu que ${app_name} acesse seus contatos locais
Nenhum resultado
Salas
- Lista de salas
+ Diretório de salas
Nenhuma sala
Nenhuma sala pública disponível
- %d usuário
- %d usuários
- Enviar registros
- Enviar registros da falha
- Enviar recorte de tela
- Relatar um erro
- Por favor, descreva o erro. O que você fez\? O que você esperava ocorrer\? O que aconteceu\?
- Descreva o seu problema aqui
- Para diagnosticar problemas, os registros deste cliente serão enviados juntos deste relatório de erros. Este relatório de erros, incluindo os registros e recortes de tela, não serão visíveis publicamente. Se você prefere enviar apenas o texto acima, por favor, desmarque:
- Você parece estar agitando o celular rapidamente. Gostaria de enviar um relatório de erro\?
- O relatório de erro foi enviado com êxito
- Falha ao enviar o relatório de erro (%s)
- Andamento (%s%%)
- O aplicativo encerrou inesperadamente da última vez. Gostaria de abrir a tela de relatórios de erros\?
+ Enviar logs
+ Enviar crash logs
+ Enviar screenshot
+ Reportar bug
+ Por favor descreva o bug. O que você fez\? O que você esperava que acontecese\? O que aconteceu na verdade\?
+ Descreva seu problema aqui
+ A fim de diagnosticar problemas, logs deste cliente serão enviados com este reporte de bug. Este reporte de bug, incluindo os logs e o screenshot, não será visível publicamente. Se você prefere somente enviar o texto acima, por favor desmarque:
+ Você parece estar agitando o celular em frustração. Gostaria de abrir a tela de reporte de bug\?
+ O reporte de bug tem sido enviado com sucesso
+ O reporte de bug falhou para enviar (%s)
+ Progresso (%s%%)
+ O aplicativo crashou da última vez. Gostaria de abrir a tela de reporte de crash\?
Enviar para
- Leitura:
- Entrar na sala
- Nome de usuário
- Criar conta
- Entrar
- Sair
- Endereço do servidor principal
- Endereço do servidor de identidade
+ Lido
+ Juntar-se a Sala
+ Nome de Usuário
+ Criar Conta
+ Fazer Login
+ Fazer Signout
+ URL de Servidor de Casa
+ URL de Servidor de Identidade
Pesquisar
- Iniciar nova conversa
- Iniciar chamada de voz
- Iniciar chamada de vídeo
+ Começar Novo Chat
+ Começar Chamada de Voz
+ Começar Chamada de Vídeo
Enviar arquivos
- Tirar uma foto ou gravar vídeo
+ Tirar foto ou vídeo
- Entrar
- Criar conta
- Enviar
+ Fazer login
+ Criar Conta
+ Submeter
Pular
- Enviar e-mail para redefinir senha
- Voltar à tela de login
- E-mail ou nome de usuário
+ Enviar Email de Reset
+ Retornar a tela de login
+ Email ou nome de usuário
Senha
Nova senha
Nome de usuário
- Endereço de e-mail
- Endereço de e-mail (opcional)
+ Endereço de email
+ Endereço de email (opcional)
Número de telefone
Número de telefone (opcional)
- Repita a senha
- Confirme sua nova senha
- Nome de usuário e/ou senha incorretos
- Nomes de usuário só podem conter letras, números, pontos, hifens e sublinhados
- Senha muita curta (mínimo de 6 caracteres)
- Falta a senha
- Este não parece ser um endereço de e-mail válido
- Este não parece ser um número de telefone válido
- Este e-mail já está cadastrado.
- Falta o endereço de e-mail
- Falta o número de telefone
- Falta o endereço de e-mail ou o número de telefone
+ Repetir senha
+ Confirmar sua nova senha
+ Nome de usuário e/ou senha incorreta(s)
+ Nomes de usuário só podem conter letras, números, pontos, hifens e underscores
+ Senha curta demais (mín 6)
+ Senha faltando
+ Isto não parece com um endereço de email válido
+ Isto não parece com um número de telefone válido
+ Este endereço de email já está definido.
+ Endereço de email faltando
+ Número de telefone faltando
+ Endereço de email ou número de telefone faltando
Token inválido
- As senhas não correspondem
- Esqueceu sua senha?
- Use opções para servidor personalizado (avançado)
- Por favor, verifique o seu e-mail para continuar a inscrição
- Atualmente, registrar-se com e-mail e número de telefone ao mesmo tempo não é possível. Apenas o número de telefone será levado em consideração.
+ Senhas não batem
+ Esqueceu senha\?
+ Usar opções de servidor personalizado (avançado)
+ Por favor cheque seu email para continuar registro
+ Registro com email e número de telefone de vez não é suportado ainda até que a api exista. Somente o número de telefone será levado em consideração.
\n
-\nNo entanto, você pode adicionar o endereço de e-mail ao seu perfil nas configurações.
- Este servidor local quer se certificar de que você não é um robô
- Nome de usuário indisponível
- Servidor principal:
- Servidor de identidade:
- Eu verifiquei o meu endereço de e-mail
- Para redefinir sua senha, digite o endereço de e-mail vinculado à sua conta:
- O e-mail vinculado à sua conta precisa ser informado.
- Uma nova senha precisa ser inserida.
- Um e-mail foi enviado para %s. Após clicar no link contido no e-mail, clique abaixo.
- Falha ao confirmar o endereço de e-mail: certifique-se de clicar no link do e-mail
- Sua senha foi alterada.
+\nVocê pode adicionar seu email a seu perfil em configurações.
+ Este Servidor de Casa gostaria de assegurar que você não é um robô
+ Nome de usuário em uso
+ Servidor de Casa:
+ Servidor de Identidade:
+ Eu tenho verificado meu endereço de email
+ Para resettar sua senha, entre o endereço de email linkado a sua conta:
+ O endereço de email linkado a sua conta deve ser entrado.
+ Uma nova senha deve ser entrada.
+ Um email tem sido enviado para %s. Uma vez que tenha seguido o link que ele contém, clique abaixo.
+ Falha ao verificar endereço de email: assegure-se que clicou no link no email
+ Sua senha tem sido resettada.
\n
-\nVocê foi desconectado de todas as sessões e não receberá mais notificações. Para reativar as notificações, faça login novamente em cada aparelho.
+\nVocê tem sido feito logout de todas as sessões e não vai mais receber notificações push. Para reativar notificações, faça re-login em cada dispositivo.
- O endereço precisa começar com http[s]://
- Não foi possível fazer login: erro de rede
- Não foi possível fazer login
- Não foi possível criar conta: erro de rede
- Não foi possível criar conta
- Não foi possível criar conta: falha na verificação de posse do e-mail
- Por favor, digite um endereço válido
- Nome de usuário ou senha inválido
+ URL deve começar com http[s]://
+ Incapaz de fazer login: Erro de rede
+ Incapaz de fazer login
+ Incapaz de registrar: Erro de rede
+ Incapaz de registrar
+ Incapaz de registrar: falha de posse de email
+ Por favor entre um URL válido
+ Nome de usuário/senha inválida(o)
O token de acesso especificado não foi reconhecido
- JSON mal formatado
- Não contem um JSON válido
- Muitas solicitações foram enviadas
- Este nome de usuário já está em uso
- O link no e-mail ainda não foi clicado
+ JSON malformado
+ Não continha JSON válido
+ Requisições demais tem sido enviadas
+ Este nome de usuário já é usado
+ O link de email que não tem sido clicado ainda
- Lista de confirmações de leitura
+ Lista de Recibos de Leitura
Enviar como
@@ -457,7 +457,7 @@
Pequeno
Cancelar o download?
- Cancelar o envio\?
+ Cancelar o upload\?
%d s
%1$dm %2$ds
@@ -465,272 +465,273 @@
Hoje
Nome da sala
- Descrição da sala
+ Tópico da sala
- Chamada aceita
- Iniciando chamada…
- Chamada encerrada
+ Chamada conectada
+ Chamada conectando…
+ Chamada terminada
Chamando…
- Recebendo chamada
- Recebendo chamada de vídeo
- Recebendo chamada de voz
- Chamada em andamento…
- A pessoa não atendeu a chamada.
- A conexão à mídia falhou
- Não foi possível iniciar a câmera
- chamada atendida em outro lugar
+ Chamada Recebendo
+ Chamada de Vídeo Recebendo
+ Chamada de Voz Recebendo
+ Chamada Em Progresso…
+ O lado remoto falhou ao atender.
+ Conexão de Mídia Falhou
+ Não é possível inicializar a câmera
+ chamada atendida em algum outro lugar
- Tirar uma foto ou gravar vídeo
+ Tirar uma foto ou um vídeo
Não é possível gravar vídeo
Informação
- ${app_name} precisa de permissão para acessar sua galeria de fotos e vídeos para enviar e salvar anexos.
+ ${app_name} precisa de permissão para acessar sua biblioteca de fotos e vídeos para enviar e salvar anexos.
\n
-\nPor favor, permita o acesso na próxima tela para poder enviar arquivos do seu celular.
- ${app_name} necessita permissão para acessar sua câmera para poder tirar fotos e fazer chamadas de vídeo.
+\nPor favor permita acesso no próximo pop-up para ser capaz de enviar arquivos do seu celular.
+ ${app_name} precisa de permissão para acessar sua câmera para tirar fotos e chamadas de vídeo.
"
\n
-\nPor favor, permita o acesso na próxima tela para fazer a chamada."
- ${app_name} necessita permissão para acessar seu microfone para realizar chamadas de áudio.
+\nPor favor permita acesso no próximo pop-up para ser capaz de fazer a chamada."
+ ${app_name} precisa de permissão para acessar seu microfone para performar chamadas de áudio.
"
\n
-\nPor favor, permita o acesso na próxima tela para fazer a chamada."
- ${app_name} necessita permissão para acessar sua câmera e seu microfone para fazer chamadas de vídeo.
+\nPor favor permita acesso no próximo pop-up para ser capaz de fazer a chamada."
+ ${app_name} precisa de permissão para acessar sua câmera e seu microfone para performar chamadas de vídeo.
\n
-\nPor favor, permita o acesso na próxima tela para fazer a chamada.
- ${app_name} precisa de permissão para acessar os seus contatos para poder encontrar outros usuários a partir de seus e-mails e números de telefone. Se você concordar em usar a sua lista de contatos para esse propósito, permita o acesso na próxima janela pop-up.
- ${app_name} precisa de permissão para acessar os seus contatos para poder encontrar outros usuários a partir de seus e-mails e números de telefone.
+\nPor favor permita acesso no próximo pop-up para ser capaz de fazer a chamada.
+ ${app_name} pode checar seu livro de endereços para achar outras(os) usuárias(os) de Matrix baseado em seus email e números de telefone. Se você concorda em compartilhar seu livro de endereços para este propósito, por favor permita acesso no próximo pop-up.
+ ${app_name} pode checar seu livro de endereços para encontrar outras(os) usuásias(os) de Matrix baseado em seus email e números de telefone.
\n
-\nVocê concorda em usar a sua lista de contatos para esse propósito\?
- Desculpe. A ação não foi realizada, por falta de permissão
+\nVocê concorda em compartilhar seu livro de endereços para este propósito\?
+ Desculpe. Ação não performada, devido a permissões faltando
Salvo
- Salvar nos downloads?
+ Salvar em downloads\?
SIM
NÃO
Continuar
- Apagar
- Entrar
- Visualizar
- Recusar
+ Remover
+ Juntar-se
+ Previsualizar
+ Rejeitar
- Ir para a primeira mensagem não lida.
+ Pular para primeira mensagem não-lida.
- Você foi convidada(o) a entrar nesta sala por %s
- Este convite foi enviado a %s, que não está associado com esta conta.
-\nVocê pode querer fazer login com uma conta diferente, ou adicionar este e-mail à sua conta.
- Você está tentando acessar %s. Quer entrar na sala para poder participar da conversa?
+ Você tem sido convidada(o) a juntar-se a esta sala por %s
+ Este convite foi enviado a %s, que não está associada(o) com esta conta.
+\nVocê pode desejar fazer login com uma conta diferente, ou adicionar este email a sua conta.
+ Você está tentando acessar %s. Gostaria de se juntar a fim de participar da discussão\?
uma sala
- Esta é uma pré-visualização desta sala. Interações com esta sala estão desativadas.
+ Esta é uma previsualização desta sala. Interações de sala têm sido desativadas.
- Nova conversa
- Adicionar uma pessoa
- 1 participante
+ Novo Chat
+ Adicionar membro
+ 1 membro
Sair da sala
- Tem certeza de que deseja sair da sala\?
- Deseja remover %s desta conversa\?
+ Você tem certeza que quer sair da sala\?
+ Você tem certeza que quer remover %s deste chat\?
Criar
Online
Offline
Ocioso
- FERRAMENTAS DE ADMINISTRAÇÃO
+ FERRAMENTAS DE ADMIN
CHAMADA
- Conversas
+ Mensagens Diretas
SESSÕES
Convidar
- Sair da sala
+ Sair desta sala
Remover desta sala
- Banir da sala
- Remover banimento
- Redefinir como usuário normal
- Tornar moderador
+ Banir
+ Desbanir
+ Resettar a usuária(o) normal
+ Tornar moderador(a)
Tornar admin
- Bloquear
- Desbloquear
- ID de usuário, nome e sobrenome ou e-mail
+ Ignorar
+ Designorar
+ ID de usuário, Nome ou email
Mencionar
- Mostrar lista de sessões
- Você não poderá desfazer esta alteração, já que você está promovendo este usuário para ter o mesmo nível de permissões que você.
-\nTem certeza\?
- "Você tem certeza que quer convidar %s para esta conversa?"
+ Mostrar Lista de Sessões
+ Você não vai ser capaz de desfazer esta mudança já que você está promovendo a(o) usuária(o) para ter o mesmo nível de poder que você.
+\nVocê tem certeza\?
+ Você tem certeza que quer convidar %s para este chat\?
Convidar por ID
CONTATOS LOCAIS (%d)
- Apenas usuários Matrix
- Convidar pessoa por sua ID
- Por favor, digite um ou mais endereços de e-mail ou ID Matrix
- E-mail ou ID Matrix
+ Usuárias(os) Matrix somente
+ Convidar usuária(o) por ID
+ Por favor entre um ou mais endereços de email ou ID Matrix
+ Email ou ID Matrix
Pesquisar
%s está digitando…
%1$s & %2$s estão digitando…
- %1$s & %2$s & outros estão digitando…
- Digite uma mensagem criptografada…
- Digite uma mensagem (não criptografada)…
- A conexão com o servidor se perdeu.
+ %1$s & %2$s & outras(os) estão digitando…
+ Envie uma mensagem encriptada…
+ Envie uma mensagem (não-encriptada)…
+ Conectividade ao servidor tem sido perdida.
Mensagens não enviadas. %1$s ou %2$s agora?
- Mensagens não enviadas por causa da presença de sessões desconhecidas. %1$s ou %2$s agora\?
+ Mensagens não enviadas devido a sessões desconhecidas estarem presentes. %1$s ou %2$s agora\?
Reenviar todas
Cancelar todas
- Reenviar mensagens não enviadas
- Apagar mensagens não enviadas
+ Reenviar mensagens não-enviadas
+ Deletar mensagens não-enviadas
Arquivo não encontrado
- Você não tem permissão para digitar nesta sala
+ Você não tem permissão para postar nesta sala
Confiar
Não confiar
- Sair da Conta
- Bloquear
+ Fazer logout
+ Ignorar
Impressão digital (%s):
- Não foi possível confirmar a identidade do servidor remoto.
- Isso pode significar que alguém está interceptando suas mensagens de forma maliciosa, ou então o seu celular não confia no certificado fornecido pelo servidor remoto.
- Se o administrador do servidor disse que isso era esperado, verifique se a impressão digital abaixo é a mesma que a impressão digital que ele forneceu a você.
- Você tinha um certificado confiável para o seu telefone, mas ele mudou. Isso é ALTAMENTE INCOMUM. É recomendável que você NÃO ACEITE este novo certificado.
- O certificado foi alterado de um anteriormente confiável para um que não é confiável. O servidor pode ter renovado seu certificado. Entre em contato com o administrador do servidor para obter a impressão digital esperada.
- Apenas aceite o certificado se o administrador do servidor publicou uma impressão digital que é idêntica a que está acima.
+ Não foi possível verificar identidade de servidor remoto.
+ Isto poderia significar que alguém está maliciosamente interceptando seu tráfico, ou que seu celular não confia no certificado provido pelo servidor remoto.
+ Se o(a) administrador(a) do servidor tem dito que isto é esperado, assegure que a impressão digital abaixo corresponde à impressão digital provida por ele(a).
+ O certificado tem mudado de um que era confiado por seu telefone. Isto é ALTAMENTE INCOMUM. É recomendado que você NÃO ACEITE este novo certificado.
+ O certificado tem sido mudado de um previamente confiado para um que não é confiado. O servidor pode ter renovado seu certificado. Conacte o(a) administrador(a) do servidor para a impressão digital esperada.
+ Somente aceite o certificado se o(a) administrador(a) do servidor tem publicado uma impressão digital que bate com a acima.
- Detalhes da sala
+ Detalhes da Sala
Pessoas
Arquivos
Configurações
- ID mal formatado. Precisa ser um endereço de e-mail ou um ID Matrix, como \'@participantelocal:dominio\'
- CONVIDADOS
- ENTRARAM
+ ID malformado. Devia ser um endereço de email ou um ID Matrix como \'@partlocal:dominio\'
+ CONVIDADAS(OS)
+ SE JUNTARAM
- Motivo de denunciar este conteúdo
- Deseja ocultar todas as mensagens deste usuário\?
+ Razão por reportar este conteúdo
+ Você quer esconder todas as mensagens deste usuário\?
\n
-\nEsta ação irá reiniciar o aplicativo e poderá demorar um pouco.
- Cancelar envio
- Cancelar download
+\nNote que esta ação irá reiniciar o app e pode levar algum tempo.
+ Cancelar Upload
+ Cancelar Download
Pesquisar
- Pesquisar participantes da sala
+ Filtrar membros da sala
Nenhum resultado
SALAS
MENSAGENS
PESSOAS
ARQUIVOS
- ENTRAR
- LISTA PÚBLICA
+ JUNTAR-SE
+ DIRETÓRIO
FAVORITOS
SALAS
BAIXA PRIORIDADE
CONVITES
- Iniciar conversa
+ Começar chat
Criar sala
- Entrar na sala
- Entrar em uma sala
- Digite o ide ou o apelido de uma sala
+ Juntar-se a sala
+ Juntar-se a uma sala
+ Digite um id de sala ou um alias de sala
- Pesquisar na lista pública
- Buscando na lista…
+ Navegar diretório
+ Pesquisando diretório…
Favoritar
- Despriorizar
- Conversa direta
- Sair da conversa
+ Des-prioritizar
+ Chat Direto
+ Sair de Conversa
Esquecer
Mensagens
Configurações
Versão
- Termos e condições
- Licenças de terceiros
- Direito autoral
+ Termos & condições
+ Notas de terceiros
+ Direito de autor
Política de privacidade
- Foto de perfil
- Nome e sobrenome
- E-mail
- Adicionar endereço de e-mail
+ Imagem de Perfil
+ Nome de Exibição
+ Email
+ Adicionar endereço de email
Telefone
Adicionar número de telefone
- Mostrar informações do aplicativo nas configurações do sistema.
- Informações sobre o aplicativo
- Receba notificações de novas mensagens
- Ativar notificações nesta sessão
- Acender a tela por 3 segundos
- Mensagens em conversas individuais
- Mensagens em salas
- Quando eu for convidada(o) a uma sala
- Recebendo chamada
- Mensagens enviadas por bots
- Sincronização em segundo plano
- Ativar a sincronização em segundo plano
- Tempo expirado na solicitação de sincronização
- Demora entre cada solicitação
+ Mostrar info de aplicativo nas configurações de sistema.
+ Info de aplicativo
+ Ativar notificações para esta conta
+ Ativar notificações para esta sessão
+ Ligar a tela por 3 segundos
+ Mnsgns em chats um-a-um
+ Mnsgns em chats de grupo
+ Quando eu sou convidada(o) a uma sala
+ Convites de chamada
+ Mensagens enviadas por bot
+ Sincronização no background
+ Ativar sinc no background
+ Timeout de requisição de sinc
+ Delay entre casa Sinc
Versão
- Versão do olm
- Termos e condições
- Licenças de terceiros
- Direitos autorais
+ versão de olm
+ Termos & condições
+ Notas de terceiros
+ Direitos de autor
Política de privacidade
- Limpar cache e recarregar
+ Limpar cache
- Configurações de usuário
+ Configurações de usuária(o)
Notificações
- Usuários bloqueados
- Outros
- Avançado
+ Usuárias(os) ignoradas(os)
+ Outras
+ Avançadas
Criptografia
- Aparelhos notificados
+ Alvos de Notificação
Contatos locais
Permissão de acesso a contatos
- País da agenda de contatos
- Página inicial
+ País de livro de telefone
+ Dsiplay home
Fixar salas com notificações perdidas
- Fixar salas com mensagens não lidas
+ Fixar salas com mensagens não-lidas
Sessões
- Detalhes da sessão
+ Informação de sessão
ID
- Nome
- Nome público do aparelho
- Visto por último às
- %1$s em %2$s
- Esta operação exige autenticação adicional.\nPara continuar, digite sua senha.
+ Nome Público
+ Atualizar Nome Público
+ Visto por último
+ %1$s @ %2$s
+ Esta operação requer autenticação adicional.
+\nPara continuar, por favor entre sua senha.
Autenticação
Senha:
- Enviar
- Conectado como
- Servidor Principal (Home Server)
- Servidor de identidade
- Confirmação pendente
- Por favor, verifique o seu e-mail e clique no link que está lá. Feito isso, clique em continuar.
- Não não foi possível confirmar o seu endereço de e-mail. Por favor, verifique o seu e-mail e clique no link que ele contém. Feito isso, clique em continuar.
- Este endereço de e-mail já está em uso.
- Este endereço de e-mail não foi encontrado.
+ Submeter
+ Feito login como
+ Servidor de Casa
+ Servidor de Identidade
+ Verificação Pendente
+ Por favor cheque seu email e clique no link que ele contém. Uma vez que isto for feito, clique em continuar.
+ Incapaz de verificar endereço de email. Por favor cheque seu email e clique no link que ele contém. Uma vez que isto for feito, clique em continuar.
+ Este endereço de email já está em uso.
+ Este endereço de email não foi encontrado.
Este número de telefone já está em uso.
- Alterar senha
+ Mudar senha
Senha atual
- Nova senha
- Confirme a nova senha
- Não consegui atualizar a senha
- Sua senha foi atualizada
+ Senha nova
+ Confirmar senha nova
+ Falha ao atualizar senha
+ Sua senha tem sido atualizada
Mostrar todas as mensagens de %s\?
\n
-\nNote que esta ação irá reiniciar o aplicativo e pode levar algum tempo.
- Deseja deixar de notificar este aparelho\?
- Deseja remover o %1$s %2$s\?
+\nNote que esta ação vai reiniciar o app e pode levar algum tempo.
+ Você tem certeza que quer remover este alvo de notificação\?
+ Você tem certeza que quer remover o %1$s %2$s\?
Escolha um país
País
- Por favor, escolha um país
+ Por favor escolha um país
Número de telefone
Número de telefone inválido para o país selecionado
- Confirmação do telefone
- "Nós enviamos um SMS com um código de ativação. Por favor, digite este código abaixo."
- Digite o código de ativação
- Erro ao validar o seu número de telefone
+ Verificação de telefone
+ Nós temos enviado um SMS com um código de ativação. Por favor entre este código abaixo.
+ Entre um código de ativação
+ Erro enquanto validando seu número de telefone
Código
- Foto da sala
- Nome da sala
- Descrição
- Etiqueta da sala
+ Foto da Sala
+ Nome da Sala
+ Tópico
+ Etiqueta da Sala
Etiquetada como:
Favoritar
@@ -738,124 +739,124 @@
Nenhuma
Acesso e visibilidade
- Exibir esta sala na lista pública de salas
- Acesso à sala
- Legibilidade do histórico da sala
- Quem pode ler o histórico de mensagens?
+ Listar esta sala em diretório de salas
+ Acesso a Sala
+ Legibilidade de Histórico da Sala
+ Quem pode ler o histórico\?
Quem pode acessar esta sala?
Qualquer pessoa
- Apenas participantes (a partir do momento em que esta opção foi escolhida)
- Apenas participantes (desde que foram convidados)
- Apenas participantes (desde que entraram na sala)
+ Membros somente (desde o ponto no tempo de selecionar esta opção)
+ Membros somente (desde que eles foram convidados)
+ Membros somente (desde que eles se juntaram)
- Para fazer um link para uma sala, ela precisa ter um endereço.
- Apenas as pessoas que foram convidadas
+ Para linkar a uma sala ela deve ter um endereço.
+ Somente pessoas que têm sido convidadas
Qualquer pessoa que saiba o link da sala, exceto visitantes
Qualquer pessoa que saiba o link da sala, incluindo visitantes
- Usuários banidos
+ Usuárias(os) banidas(os)
- Avançado
+ Avançadas
ID interno desta sala
Endereços
- Laboratórios
- Estes são recursos experimentais que podem quebrar de forma inesperada. Use com cautela.
- Criptografia de ponta a ponta
- A criptografia de ponta a ponta está ativada
- Você precisa desconectar para poder ativar a criptografia.
- Criptografar apenas para sessões confirmadas
- Nunca enviar mensagens criptografadas para sessões não confirmadas nesta sala, a partir desta sessão.
+ Labs
+ Estes são recursos experimentais que podem quebrar de maneiras inesperadas. Use com cuidado.
+ Encriptação Ponta-a-Ponta
+ Encriptação Ponta-a-Ponta está ativa
+ Você precisa fazer logout para ser capaz de ativar a encriptação.
+ Encriptar para sessões verificadas somente
+ Nunca enviar mensagens encriptadas para sessões não-confirmadas nesta sala desta sessão.
- Esta sala não tem endereços locais
- Novo endereço (p.ex: #foo:matrix.org")
- Formato inválido de alias
+ Esta sala não tem nenhum endereço local
+ Novo endereço (e.g. #foo:matrix.org)
+ Formato de alias inválido
\'%s\' não é um formato válido para um alias
- Você não terá endereço principal especificado para esta sala.
- Alertas do endereço principal
+ Você não vai ter nenhum endereço principal especificado para esta sala.
+ Avisos de endereço principal
Definir como endereço principal
- Retirar este endereço como principal
- Copiar o ID desta sala
- Copiar o endereço desta sala
- A criptografia está ativada nesta sala.
- A criptografia está desativada nesta sala.
- Ativar criptografia·
-\n(atenção: não é possível desativar depois!)
+ Des-definir como endereço principal
+ Copiar ID de Sala
+ Copiar Endereço da Sala
+ Encriptação está ativada nesta sala.
+ Encriptação está desativada nesta sala.
+ Ativar encriptação
+\n(atenção: não pode ser desativado de novo!)
- Lista
+ Diretório
- %s tentou carregar um trecho específico da conversa desta sala, mas não conseguiu.
+ %s estava tentando carregar um ponto específico na timeline desta sala mas foi incapaz de encontrá-lo.
- Informação sobre a criptografia de ponta a ponta
+ Informação de encriptação ponta-a-ponta
Informação do evento
- ID de usuária/o
- Chave de identidade curve25519
- Chave de impressão digital ed25519 reivindicada
+ Id de usuária(o)
+ Chave de identidade Curve25519
+ Chave de impressão digital Ed25519 clamada
Algoritmo
ID de Sessão
- Erro de descriptografia
- Informação sobre a sessão do remetente
- Nome público do aparelho
- Nome
- ID da sessão
+ Erro de decriptação
+ Informação de sessão do(a) enviador(a)
+ Nome público
+ Nome público
+ ID de sessão
Chave da sessão
- Confirmação
- Impressão digital ed25519
- Exportar chaves de sala E2E
+ Verificação
+ Impressão digital Ed25519
+ Exportar chaves de sala PAP
Exportar chaves de sala
Exportar as chaves para um arquivo local
Exportar
- Digite a frase secreta
- Confirme a frase secreta
- As chaves E2E da sala foram salvas em \'%s\'.
+ Entrar frasepasse
+ Confirmar frasepasse
+ As chaves de sala PAP têm sido salvas em \'%s\'.
\n
-\nAtenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.
- Importar chaves de sala E2E
+\nAviso: este arquivo pode ser deletado se o aplicativo for desinstalado.
+ Importar chaves de sala PAP
Importar chaves de sala
Importar as chaves de um arquivo local
Importar
- Criptografar apenas para sessões confirmadas
- Nunca enviar mensagens criptografadas para sessões não confirmadas, a partir desta sessão.
- Não confirmado
- Confirmado
+ Encriptar para sessões confirmadas somente
+ Nunca enviar mensagens encriptadas para sessões não-verificadas desta sessão.
+ Não Verificada
+ Verificada
Na lista negra
sessão desconhecida
nenhuma
- Confirmar
- Marcar como não confirmado
+ Verificar
+ Desverificar
Colocar na lista negra
Retirar da lista negra
- Confirmar sessão
- Compare as seguintes informações com aquelas na sessão do outro usuário e confirme:
- Se não corresponderem, a segurança da sua comunicação pode estar comprometida.
- Eu confirmo que as chaves são iguais
+ Verificar sessão
+ Confirme ao comparar o seguinte com as Configurações de Usuária(o) em sua outra sessão:
+ Se não baterem, a segurança de sua comunicação pode estar comprometida.
+ Eu verifico que as chaves batem
- Esta sala contém sessões desconhecidas
- Esta sala contém sessões desconhecidas que não foram confirmadas.
-\nIsso significa que não há garantia de que estas sessões realmente pertencem aos usuários identificados.
-\nRecomendamos que você faça o processo de confirmação para cada sessão desconhecida antes de continuar, mas você pode reenviar a mensagem sem confirmar os aparelhos, se preferir.
+ Sala contém sessões desconhecidas
+ Esta sala contém sessões desconhecidas que não têm sido verificadas.
+\nIsto significa que não há nenhuma garantia que as sessões pertencem às(aos) usuárias(os) às(aos) quais elas clamam pertencer.
+\nNós recomendamos que você passe pelo processo de verificação para cada sessão antes de continuar, mas você pode reenviar a mensagem sem verificar se você preferir.
\n
-\nSessões desconhecidas nesta sala:
+\nSessões desconhecidas:
- Escolha uma lista pública de salas
+ Selecione um diretório de salas
O servidor pode estar indisponível ou sobrecarregado
- Digite um servidor local, a partir do qual serão listadas as salas públicas
- Endereço do servidor principal
- Todas as salas com o servidor %s
- Todas as salas nativas em %s
+ Digite um servidor de casa para de onde listar salas públicas
+ URL de servidorcasa
+ Todas as salas em servidor %s
+ Todas as salas nativas de %s
- Pesquisar no histórico
+ Pesquisar por histórico
Offline
- Lista de usuários
- LISTA DE USUÁRIOS (%s)
- Iniciar com o sistema
- Esvaziar o cache de mídia
+ Diretório de usuários
+ DIRETÓRIO DE USUÁRIOS (%s)
+ Começar em boot
+ Limpar cache de mídia
Manter mídia
- Mostrar a hora para todas as mensagens
+ Mostrar timestamps para todas as mensagens
Modo de economia de dados
- Aparência
- Idioma
- Escolha o idioma
+ Interface de usuária(o)
+ Língua
+ Escolher língua
3 dias
1 semana
1 mês
@@ -869,112 +870,112 @@
Maior
Ainda maior
Gigantesco
- Tema claro
- Tema escuro
- Tema preto
+ Tema Claro
+ Tema Escuro
+ Tema Preto
Sincronizando…
- Escutando eventos
- Notificações com som
+ À escuta por eventos
+ Notificações barulhentas
Notificações silenciosas
- Relatar um erro
- Detalhes da comunidade
+ Reporte de bug
+ Detalhes de Comunidade
Carregando…
Sair
Comunidades
Filtrar nomes de comunidades
Convidar
Comunidades
- Nenhuma comunidade
- Tem certeza de que deseja iniciar uma nova conversa com %s\?
- Tem certeza de que deseja iniciar uma chamada de voz\?
- Tem certeza de que deseja iniciar uma chamada de vídeo\?
+ Nenhum grupo
+ Você tem certeza que quer começar um novo chat com %s\?
+ Você tem certeza que quer começar uma chamada de voz\?
+ Você tem certeza que quer começar uma chamada de vídeo\?
Tirar foto
- Gravar vídeo
- Lista de comunidades
- Chamada de voz
- Banir o usuário resultará em sua remoção desta sala, impedindo-o de entrar nela novamente.
- Todas as mensagens novas (com som)
- Todas as mensagens novas
- Apenas @menções
- Silenciar
- Adicionar o Element na tela inicial
+ Tirar vídeo
+ Lista de Grupos
+ Chamar
+ Banir usuária(o) vai expulsá-la(o) desta sala e preveni-la(o) de se juntar de novo.
+ Todas as mensagens (barulhento)
+ Todas as mensagens
+ Menções somente
+ Mudo
+ Adicionar a tela de Início
Som de notificação
- Mensagens que contenham o meu nome e sobrenome
- Mensagens que contenham o meu nome de usuário
- Visualização prévia do endereço
- Mostrar a hora no formato de 12 horas
- Vibrar ao mencionar um usuário
- Estatísticas de uso
- Ícone
+ Mnsgns contendo meu nome de exibição
+ Mnsgns contando meu nome de usuário
+ Previsualização de URL emlinha
+ Mostrar timestamps em formato de 12 horas
+ Vibrar ao mencionar um(a) usuário(a)
+ Analítica
+ Flair
Notificações
- Esta sala não está mostrando ícones de nenhuma comunidade
- Nova ID da comunidade (p.ex: +foo:matrix.org)
- ID de comunidade inválido
- \'%s\' não é um ID de comunidade válido
- Você necessita ter permissões para poder gerenciar os widgets desta sala
- A criação do widget falhou
- Crie chamadas em grupo com jitsi
- Você tem certeza que quer apagar este widget desta sala?
+ Esta sala não está mostrando flair para nenhuma comunidade
+ Nova ID de comunidade (e.g. +foo:matrix.org)
+ ID de comunidade inválida
+ \'%s\' não é uma ID de comunidade válida
+ Você precisa de permissão para gerenciar widgets nesta sala
+ Criação de widget tem falhado
+ Criar chamadas de conferência com jitsi
+ Você tem certeza que quer deletar o widget desta sala\?
- Impossível criar o widget.
- O envio do pedido falhou.
- O nível de permissão precisa ser um número inteiro positivo.
+ Incapaz de criar widget.
+ Falha ao enviar requisição.
+ Nível de poder deve ser um inteiro positivo.
Você não está nesta sala.
- Você não tem permissões para fazer isso nesta sala.
- O pedido veio sem o room_id.
- O pedido veio sem o user_id.
- A sala %s não está visível.
- Adicionar aplicativos Matrix
- Usar a câmera nativa
+ Você não tem permissão para fazer isso nesta sala.
+ room_id faltando em requisição.
+ user_id faltando em requisição.
+ Sala %s não está visível.
+ Adicionar apps Matrix
+ Usar câmera nativa
- Você adicionou uma nova sessão \'%s\', que está solicitando as chaves de criptografia.
- Sua sessão não confirmada \'%s\' está solicitando as chaves de criptografia.
- Iniciar a confirmação
- Compartilhar sem confirmar
- Ignorar a solicitação
+ Você adicionou uma nova sessão \'%s\', que está requisitando chaves de encriptação.
+ Sua sessão não-verificada \'%s\' está requisitando chaves de encriptação.
+ Começar verificação
+ Compartilhar sem verificar
+ Ignorar requisição
- Atenção!
- Chamadas em grupo estão em desenvolvimento, portanto podem não ser estáveis.
+ Aviso!
+ Chamamento de conferência está em desenvolvimento e pode não ser estável.
Erro de comando
- Comando desconhecido: %s
+ Comando irreconhecido: %s
- Desativado
- Ativado com som
- Mensagem criptografada
+ Desativada
+ Barulhenta
+ Mensagem encriptada
Criar
- Criar comunidade
+ Criar Comunidade
Nome da comunidade
Exemplo
- ID da comunidade
+ ID de comunidade
exemplo
- Início
+ Home
Pessoas
Salas
- Sem ninguém
+ Nenhum(a) usuário(a)
Salas
- Entrou
- em que foi convidada/o
- Pesquisar participantes da comunidade
- Filtrar salas da comunidade
- O(A) administrador(a) desta comunidade não definiu uma descrição longa da mesma.
- Você foi removido da sala %1$s por %2$s
- Você foi banido da sala %1$s devido à %2$s
- Motivo: %1$s
- Entrar novamente
- Esquecer a sala
+ Juntou-se
+ Convidada(o)
+ Filtrar membros do grupo
+ Filtrar salas do grupo
+ O(a) administrador(a) da comunidade não tem provido uma descrição longa para esta comunidade.
+ Você foi expulsa(o) de %1$s por %2$s
+ Você foi banida(o) de %1$s por %2$s
+ Razão: %1$s
+ Rejuntar-se
+ Esquecer sala
Ações
Abrir cabeçalho
Sincronizando…
- - %d participante ativo
- - %d participantes ativos
+ - %d membro ativo
+ - %d membros ativos
- - %d participante
- - %d participantes
+ - %d membro
+ - %d membros
- %d nova mensagem
@@ -989,17 +990,17 @@
- %1$s salas encontradas para %2$s
- - %d alteração na filiação
- - %d alterações na filiação
+ - %d mudança de filiação
+ - %d mudanças de filiação
- Lista de participantes
+ Listar membros
- - %d mensagem não lida
- - %d mensagens não lidas
+ - %d mensagem notificada não-lida
+ - %d mensagens notificadas não-lidas
- - %d mensagem não lida
- - %d mensagens não lidas
+ - %d mensagem notificada não-lida
+ - %d mensagens notificadas não-lidas
- %d sala
@@ -1011,32 +1012,32 @@
- %d widgets ativos
- Foto de perfil para a confirmação de leitura
- Foto de perfil para avisos
- Foto de perfil
- Agite rapidamente para relatar um erro
+ Avatar de recibo
+ Avatar de nota
+ Avatar
+ Agite com raiva para reportar bug
Normal
Privacidade reduzida
- O app necessita de permissão para rodar em segundo plano
- Enviar uma figurinha
+ O app precisa de permissão para rodar no background
+ Enviar um sticker
Licenças de terceiros
- Baixar
+ Fazer Download
Falar
Limpar
- Devido à falta de permissões, essa ação não é possível.
- Alertas do sistema
- Se possível, escreva a descrição em inglês, por favor.
- Enviar áudio
- Enviar uma figurinha
- No momento, você não tem nenhum pacote de figurinhas ativado.
+ Devido a permissões faltando, esta ação não é possível.
+ Alertas de Sistema
+ Se possível, por favor escreva a descrição em Inglês.
+ Enviar voz
+ Enviar sticker
+ Atualmente você não tem nenhum pacote de stickers ativado.
\n
-\nQuer adicionar alguns agora\?
+\nAdicionar alguns agora\?
continuar com…
- Desculpe, nenhum aplicativo externo foi encontrado para concluir esta ação.
- Solicitar novamente as chaves de criptografia das suas outras sessões.Pedir novamente as chaves de criptografia de seus outros dispositivos.
+ Desculpe, nenhum aplicativo externo foi encontrado para completar esta ação.
+ Re-requisitar chaves de encriptação de suas outras sessões.
Requisição de chave enviada.
Requisição enviada
- Por favor, inicie o ${app_name} em outro aparelho que possa descriptografar a mensagem, de modo que ele possa enviar as chaves para esta sessão.
+ Por favor lance ${app_name} num outro dispositivo que possa decriptar a mensagem para que ele possa enviar as chaves para esta sessão.
- %ds
- %ds
@@ -1050,189 +1051,189 @@
- %dh
- - %d dia
- - %d dias
+ - %dd
+ - %dd
%1$s agora
- há %1$s %2$s
+ %1$s %2$s atrás
"%1$s, "
%1$s e %2$s
%1$s %2$s
- Digite sua resposta criptografada…
- Digite sua resposta (não criptografada)…
+ Envie uma resposta encriptada…
+ Envie uma resposta (não-encriptada)…
- - %d selecionado
- - %d selecionados
+ - %d selecionada
+ - %d selecionadas
- Privacidade das notificações
- • As notificações são enviadas pelo \"Google Cloud Messaging\"
- • As notificações contém apenas metadados
- • O conteúdo das mensagens da notificação é armazenado de forma segura diretamente no servidor Matrix
- • As notificações contém metadados e os dados da mensagem
- • As notificações não exibirão o conteúdo da mensagem
- Pré-visualizar a mídia antes de enviá-la
- Desativar minha conta
+ Privacidade de notificações
+ • Notificações são enviadas via Firebase Cloud Messaging
+ • Notificações somente contêm meta dados
+ • Conteúdo de mensagem da notificação é localizado seguramente direto do servidor de casa de Matrix
+ • Notificações contêm metadados e dados de mensagem
+ • Notificações não vão mostrar conteúdo de mensagem
+ Previsualizar mídia antes de enviar
+ Desativar conta
Desativar minha conta
- Privacidade das notificações
- ${app_name} pode funcionar em segundo plano para gerenciar as suas notificações de forma segura e confidencial. Isso poderá impactar o uso da bateria.
- Conceder a permissão
- Escolha outra opção
- Enviar dados de uso
- ${app_name} coleta dados de uso anônimos para nos ajudar a melhorar o aplicativo.
- Por favor, ative o envio de dados de uso para nos ajudar a melhorar o ${app_name}.
+ Privacidade de Notificação
+ ${app_name} pode rodar no background para gerenciar suas notificações seguramente e privadamente. Isto pode afetar uso de bateria.
+ Conceder permissão
+ Escolha um outra opção
+ Enviar dados de analítica
+ ${app_name} coleta analítica anônima para nos permitir melhorar o aplicativo.
+ Por favor ative analítica para nos ajudar a melhorar ${app_name}.
Sim, eu quero ajudar!
- Você não faz parte de alguma comunidade, no momento.
- Escreva aqui…
- Um parâmetro obrigatório está faltando.
+ Você não é atualmente um membro de quaisquer comunidades.
+ Digite aqui…
+ Um parâmetro requerido está faltando.
Um parâmetro não é válido.
- Use a tecla Enter do teclado para enviar a mensagem
+ Usar tecla enter de teclado para enviar mensagem
Enviar mensagens de voz
- Mostra a ação
- Bane o usuário com a ID fornecida
- Remove o banimento do usuário com a ID fornecida
- Define o grau de poder de um(a) usuário(a)
- Retira o nível de operador(a) do(a) usuário(a) com o ID fornecido
- Convida a(o) usuária(o) com um dado ID para esta sala
- Entra na sala com o alias fornecido
- Deixa a sala
- Define a descrição da sala
- Remove o usuário com o ID fornecido
- Altera o seu nome e sobrenome
- Ativar/Desativar a formatação de texto
- Reparar a gestão de aplicativos Matrix
+ Exibe ação
+ Bane usuária(o) com id dada
+ Desbane usuária(o) com id dada
+ Define nível de poder de um(a) usuário(a)
+ Desopa usuária(o) com id dada
+ Convida usuária(o) com id dada para esta sala
+ Junta-se a sala com alias dado
+ Sair de sala
+ Definir o tópico da sala
+ Expulsa a(o) usuária(o) com id dada
+ Muda seu apelido de exibição
+ Ativar/Desativar markdown
+ Para consertar gerenciamento de Apps Matrix
- - %d participante
- - %d participantes
+ - %d membro
+ - %d membros
- %d sala
- %d salas
- Para continuar usando o Servidor de Base %1$s, você precisa revisar e aceitar os termos e condições de uso.
+ Para continuar usando o servidorcasa %1$s você deve revisar e aceitar os termos e condições.
Revisar agora
- Desativar minha conta
- Isso tornará sua conta permanentemente inutilizável. Você não conseguirá efetuar login e ninguém poderá registrar novamente o mesmo ID de usuário. Isso fará com que sua conta saia de todas as salas das quais está participando e removerá os detalhes de sua conta do servidor de identidade. Esta ação é irreversível.
+ Desativar Conta
+ Isto vai fazer sua conta permanentemente inusável. Você não vai ser capaz de fazer login, e ninguém vai poder re-registrar o mesmo ID de usuária(o). Isto vai causar sua conta sair de todas as salas em que ela está participando, e vai remover detalhes de sua conta de seu servidor de identidade. Esta ação é irreversível.
\n
-\nDesativar sua conta não faz com que, por padrão, suas mensagens enviadas sejam apagadas. Se você deseja que suas mensagens também sejam apagadas, marque a opção abaixo.
+\nDesativar sua conta não nos causa por padrão esquecer mensagens que você tem enviado. Se você gostaria que nós esqueçamos suas mensagens, por favor marque a caixa abaixo.
\n
-\nA visibilidade de mensagens na Matrix é semelhante a um e-mail. O fato de apagarmos suas mensagens significa que suas mensagens enviadas não serão compartilhadas com nenhum usuário novo ou ainda não registrado, mas os usuários registrados que já tiveram acesso a essas mensagens ainda terão acesso uma cópia delas.
- Quando minha conta for desativada, apague todas as mensagens que eu enviei (Atenção: isso fará com que futuros usuários tenham uma visão incompleta das conversas)
- Para continuar, entre com sua senha:
- Desativar minha conta
- Por favor, digite sua senha.
- Esta sala foi substituída e não está mais ativa
+\nVisibilidade de mensagem em Matrix é similar a email. Nós esquecermos suas mensagens significa que mensagens que você tem enviado não vão ser compartilhadas com nenhum usuária(o) nova(o) ou não-registrada(o), mas usuárias(os) registradas(os) que já têm acesso a estas mensagens vão ainda ter acesso à cópia delas(es).
+ Por favor esqueça todas as mensagens que eu tenho enviado quando minha conta for desativada (Aviso: isto vai causar usuárias/os futuras/os terem uma visão incompleta de conversas)
+ Para continuar, por favor entre sua senha:
+ Desativar Conta
+ Por favor entre sua senha.
+ Esta sala tem sido substituída e não está mais ativa
A conversa continua aqui
- Esta sala é a continuação de outra conversa
- Clique aqui para ver as mensagens anteriores
- Limite de recursos excedido
- Entre em contato com o(a) administrador(a)
- entre em contato com o administrador do seu serviço
- Este servidor local excedeu um dos seus limites de recursos, portanto alguns usuários não conseguirão fazer login.
- Este servidor local excedeu um de seus limites de recursos.
- Este homeserver atingiu o seu limite mensal de usuários ativos, portanto alguns usuários não conseguirão fazer login.
- Este homeserver atingiu o seu limite mensal de usuários ativos.
- Por favor, %s para que este limite seja aumentado.
- Por favor, %s para seguir usando este serviço.
- Crie uma frase secreta para criptografar as chaves exportadas. Você precisará inserir essa frase secreta para importar chaves criptografadas.
+ Esta sala é uma continuação de uma outra conversa
+ Clique aqui para ver mensagens mais antigas
+ Limite de Recurso Excedido
+ Contactar Administrador(a)
+ contacte o(a) administrador(a) de seu serviço
+ Este servidorcasa tem excedido um de seus limites de recurso, então algumas(ns) usuária(os) não vão ser capazes de fazer login.
+ Este servidorcasa tem excedido um de seus limites de recurso.
+ Este servidorcasa tem atingido seu limite de Usuárias(os) Mensalmente Ativos então algumas(ns) usuárias(os) não vão ser capazes de fazer login.
+ Este servidorcasa tem atingido seu limite de Usuárias(os) Mensalmente Ativas(os).
+ Por favor %s para ter este limite aumentado.
+ Por favor %s para continuar usando este serviço.
+ Por favor crie uma frasepasse para encriptar as chaves exportadas. Você vai precisar entrar a mesma frasepasse para ser capaz de importar as chaves.
Aceitar
Erro
- Por favor, revise e aceite as políticas deste servidor local:
+ Por favor revise e aceite as políticas deste servidor de casa:
Chamadas
- Use o toque padrão do ${app_name} para chamadas recebidas
- Toque de chamadas recebidas
- Selecione o toque de chamadas:
- Motivo
+ Usar toque default de ${app_name} para chamadas recebendo
+ Toque de chamada recebendo
+ Selecione toque para chamadas:
+ Razão
Versão %s
- Resolver problemas nas notificações
+ Resolver Problemas de Notificação
Diagnóstico de resolução de problemas
- Executar Testes
- Executando… (%1$d of %2$d)
- O diagnóstico básico está ok. Se você ainda não recebe notificações, por favor relate um erro para nos ajudar a investigar.
- Um ou mais testes falharam, tente as correções sugeridas.
- Um ou mais testes falharam, por favor relate um erro para nos ajudar a investigar.
- Configurações do sistema
- Notificações estão ativadas nas configurações do sistema.
+ Rodar Testes
+ Rodando… (%1$d of %2$d)
+ Diagnóstico básico está OK. Se você ainda não recebe notificações, por favor submita um reporte de bug para nos ajudar a investigar.
+ Um ou mais testes têm falhado, tente correção(ões) sugerida(s).
+ Um ou mais testes têm falhado, por favor submita um reporte de bug para nos ajudar a investigar.
+ Configurações de Sistema.
+ Notificações estão ativadas nas configurações de sistema.
Notificações estão desativadas nas configurações do sistema.
-\nPor favor, revise as configurações do sistema.
- Abra as Configurações
- Configurações da conta
+\nPor favor cheque configurações de sistema.
+ Abrir Configurações
+ Configurações de Conta.
Notificações estão ativadas para sua conta.
Notificações estão desativadas para sua conta.
-\nPor favor, revise as configurações da conta.
+\nPor favor cheque configurações de conta.