Merge branch 'develop' into feature/fga/message_bubbles

This commit is contained in:
ganfra 2022-02-01 10:05:51 +01:00
commit 825c2ca989
105 changed files with 1635 additions and 450 deletions

View File

@ -1,3 +1,15 @@
Changes in Element v1.3.17 (2022-01-31)
=======================================
Bugfixes 🐛
----------
- Display static map images in the timeline and improve Location sharing feature ([#5084](https://github.com/vector-im/element-android/issues/5084))
- Show the legal mention of mapbox when sharing location ([#5062](https://github.com/vector-im/element-android/issues/5062))
- Poll cannot end in some unencrypted rooms ([#5067](https://github.com/vector-im/element-android/issues/5067))
- Selecting Transfer in a call should immediately put the other person on hold until the call connects or the Transfer is cancelled. ([#5081](https://github.com/vector-im/element-android/issues/5081))
- Fixing crashes when quickly scrolling or restoring the room timeline ([#5091](https://github.com/vector-im/element-android/issues/5091))
Changes in Element 1.3.16 (2022-01-25)
======================================

View File

@ -14,7 +14,8 @@ It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-androi
[<img src="resources/img/google-play-badge.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.app)
[<img src="resources/img/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop)
Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly sanity test status: [![allScreensTest](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml/badge.svg)](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml)
# New Android SDK

View File

@ -1 +0,0 @@
Show the legal mention of mapbox when sharing location

View File

@ -1 +0,0 @@
Poll cannot end in some unencrypted rooms

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou.
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou.
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou.
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs
Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Main changes in this version: send your location to any room. Edit poll.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.17

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus.
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között.
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között.
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között.
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba.
Changelog lanjutan:

View File

@ -0,0 +1,2 @@
Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba.
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba.
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori.
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori.
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori.
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs.
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs.
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs.
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách.
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách.
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách.
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами.
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами.
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами.
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,2 @@
此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.3.13

View File

@ -0,0 +1,2 @@
此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.3.14

View File

@ -0,0 +1,2 @@
此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.3.15

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="is_rtl">true</bool>
</resources>

View File

@ -4,4 +4,6 @@
<!-- Created to detect what has to be implemented (especially in the settings) -->
<bool name="false_not_implemented">false</bool>
<bool name="is_rtl">false</bool>
</resources>

View File

@ -164,7 +164,7 @@ dependencies {
implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.42'
testImplementation libs.tests.junit
testImplementation 'org.robolectric:robolectric:4.7.3'

View File

@ -63,5 +63,5 @@ data class MessageLocationContent(
@Json(name = "org.matrix.msc1767.text") val text: String? = null
) : MessageContent {
fun getUri() = locationInfo?.geoUri ?: geoUri
fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri
}

View File

@ -74,6 +74,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
event to it
}
}
Timber.d("[PushRules] matched ${matchedEvents.size} out of ${allEvents.size}")
val allRedactedEvents = params.syncResponse.join
.asSequence()

View File

@ -373,7 +373,7 @@ dependencies {
implementation 'com.facebook.stetho:stetho:1.6.0'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.42'
// FlowBinding
implementation libs.github.flowBinding

View File

@ -195,6 +195,9 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource {
println("*** [$name] onActivityLifecycleChanged callback: $callback")
callback?.onTransitionToIdle()
}
else -> {
// do nothing, we're blocking until the activity resumes
}
}
}
}

View File

@ -17,12 +17,16 @@
package im.vector.app.ui.robot
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem
import com.google.android.material.bottomsheet.BottomSheetBehavior
import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.espresso.tools.waitUntilViewVisible
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.app.features.reactions.EmojiReactionPickerActivity
import im.vector.app.interactWithSheet
import java.lang.Thread.sleep
@ -54,7 +58,10 @@ class MessageMenuRobot(
fun addReactionFromEmojiPicker() {
clickOn(R.string.message_add_reaction)
// Wait for emoji to load, it's async now
sleep(2000)
waitUntilActivityVisible<EmojiReactionPickerActivity> {
waitUntilViewVisible(withId(R.id.emojiRecyclerView))
waitUntilViewVisible(withText("😀"))
}
clickListItem(R.id.emojiRecyclerView, 4)
autoClosed = true
}

View File

@ -20,13 +20,13 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.longClick
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.interaction.BaristaClickInteractions
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaClickInteractions.longClickOn
import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo
import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.clickMenu
import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.openMenu
@ -70,6 +70,7 @@ class RoomDetailRobot {
openMessageMenu(message) {
addQuickReaction(quickReaction)
}
waitUntilViewVisible(withText(quickReaction))
println("Open reactions bottom sheet")
// Open reactions
longClickReaction(quickReaction)
@ -103,7 +104,7 @@ class RoomDetailRobot {
private fun longClickReaction(quickReaction: String) {
withRetry {
longClickOn(quickReaction)
onView(withText(quickReaction)).perform(longClick())
}
}

View File

@ -17,24 +17,25 @@
package im.vector.app.core.epoxy.bottomsheet
import android.text.method.MovementMethod
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.bumptech.glide.request.RequestOptions
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.glide.GlideApp
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.MapTilerMapView
import im.vector.app.features.media.ImageContentRenderer
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
import org.matrix.android.sdk.api.util.MatrixItem
@ -70,7 +71,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
var time: String? = null
@EpoxyAttribute
var locationData: LocationData? = null
var locationUrl: String? = null
@EpoxyAttribute
var locationPinProvider: LocationPinProvider? = null
@ -97,17 +98,21 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
body.charSequence.findPillsAndProcess(coroutineScope) { it.bind(holder.body) }
holder.timestamp.setTextOrHide(time)
holder.mapView.isVisible = locationData != null
holder.body.isVisible = locationData == null
locationData?.let { location ->
holder.mapView.initialize {
if (holder.view.isAttachedToWindow) {
holder.mapView.zoomToLocation(location.latitude, location.longitude, 15.0)
locationPinProvider?.create(matrixItem.id) { pinDrawable ->
holder.mapView.addPinToMap(matrixItem.id, pinDrawable)
holder.mapView.updatePinLocation(matrixItem.id, location.latitude, location.longitude)
}
}
if (locationUrl == null) {
holder.body.isVisible = true
holder.mapViewContainer.isVisible = false
} else {
holder.body.isVisible = false
holder.mapViewContainer.isVisible = true
GlideApp.with(holder.staticMapImageView)
.load(locationUrl)
.apply(RequestOptions.centerCropTransform())
.into(holder.staticMapImageView)
locationPinProvider?.create(matrixItem.id) { pinDrawable ->
GlideApp.with(holder.staticMapPinImageView)
.load(pinDrawable)
.into(holder.staticMapPinImageView)
}
}
}
@ -124,6 +129,8 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
val bodyDetails by bind<TextView>(R.id.bottom_sheet_message_preview_body_details)
val timestamp by bind<TextView>(R.id.bottom_sheet_message_preview_timestamp)
val imagePreview by bind<ImageView>(R.id.bottom_sheet_message_preview_image)
val mapView by bind<MapTilerMapView>(R.id.bottom_sheet_message_preview_location)
val mapViewContainer by bind<FrameLayout>(R.id.mapViewContainer)
val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
}
}

View File

@ -21,6 +21,7 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import im.vector.app.core.services.VectorSyncService
import im.vector.app.features.session.VectorSessionStore
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
import org.matrix.android.sdk.api.session.sync.FilterService
@ -76,3 +77,5 @@ fun Session.cannotLogoutSafely(): Boolean {
// That are not backed up
!sharedSecretStorageService.isRecoverySetup())
}
fun Session.vectorStore(context: Context) = VectorSessionStore(context, myUserId)

View File

@ -29,6 +29,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.startSyncing
import im.vector.app.core.extensions.vectorStore
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.deleteAllFiles
import im.vector.app.databinding.ActivityMainBinding
@ -40,6 +41,7 @@ import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.pin.UnlockedActivity
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.session.VectorSessionStore
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.signout.hard.SignedOutActivity
import im.vector.app.features.themes.ActivityOtherThemes
@ -143,13 +145,15 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
startNextActivityAndFinish()
return
}
val onboardingStore = session.vectorStore(this)
when {
args.isAccountDeactivated -> {
lifecycleScope.launch {
// Just do the local cleanup
Timber.w("Account deactivated, start app")
sessionHolder.clearActiveSession()
doLocalCleanup(clearPreferences = true)
doLocalCleanup(clearPreferences = true, onboardingStore)
startNextActivityAndFinish()
}
}
@ -163,14 +167,14 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
}
Timber.w("SIGN_OUT: success, start app")
sessionHolder.clearActiveSession()
doLocalCleanup(clearPreferences = true)
doLocalCleanup(clearPreferences = true, onboardingStore)
startNextActivityAndFinish()
}
}
args.clearCache -> {
lifecycleScope.launch {
session.clearCache()
doLocalCleanup(clearPreferences = false)
doLocalCleanup(clearPreferences = false, onboardingStore)
session.startSyncing(applicationContext)
startNextActivityAndFinish()
}
@ -183,7 +187,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
Timber.w("Ignoring invalid token global error")
}
private suspend fun doLocalCleanup(clearPreferences: Boolean) {
private suspend fun doLocalCleanup(clearPreferences: Boolean, vectorSessionStore: VectorSessionStore) {
// On UI Thread
Glide.get(this@MainActivity).clearMemory()
@ -193,6 +197,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
pinLocker.unlock()
pinCodeStore.deleteEncodedPin()
vectorAnalytics.onSignOut()
vectorSessionStore.clear()
}
withContext(Dispatchers.IO) {
// On BG thread

View File

@ -18,6 +18,7 @@ package im.vector.app.features.analytics
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
import im.vector.app.features.analytics.plan.Identity
interface AnalyticsTracker {
/**
@ -29,4 +30,9 @@ interface AnalyticsTracker {
* Track a displayed screen
*/
fun screen(screen: VectorAnalyticsScreen)
/**
* Update user specific properties
*/
fun updateUserProperties(identity: Identity)
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 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.analytics.extensions
import im.vector.app.features.analytics.plan.Identity
import im.vector.app.features.onboarding.FtueUseCase
fun FtueUseCase.toTrackingValue(): Identity.FtueUseCaseSelection {
return when (this) {
FtueUseCase.FRIENDS_FAMILY -> Identity.FtueUseCaseSelection.PersonalMessaging
FtueUseCase.TEAMS -> Identity.FtueUseCaseSelection.WorkMessaging
FtueUseCase.COMMUNITIES -> Identity.FtueUseCaseSelection.CommunityMessaging
FtueUseCase.SKIP -> Identity.FtueUseCaseSelection.Skip
}
}

View File

@ -17,6 +17,7 @@
package im.vector.app.features.analytics.impl
import android.content.Context
import com.posthog.android.Options
import com.posthog.android.PostHog
import com.posthog.android.Properties
import im.vector.app.BuildConfig
@ -25,6 +26,7 @@ import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
import im.vector.app.features.analytics.log.analyticsTag
import im.vector.app.features.analytics.plan.Identity
import im.vector.app.features.analytics.store.AnalyticsStore
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
@ -34,6 +36,9 @@ import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
private val REUSE_EXISTING_ID: String? = null
private val IGNORED_OPTIONS: Options? = null
@Singleton
class DefaultVectorAnalytics @Inject constructor(
private val context: Context,
@ -170,6 +175,10 @@ class DefaultVectorAnalytics @Inject constructor(
?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties())
}
override fun updateUserProperties(identity: Identity) {
posthog?.identify(REUSE_EXISTING_ID, identity.getProperties().toPostHogProperties(), IGNORED_OPTIONS)
}
private fun Map<String, Any>?.toPostHogProperties(): Properties? {
if (this == null) return null

View File

@ -46,12 +46,14 @@ data class PerformanceTimer(
enum class Name {
/**
* The time spent parsing the response from an initial /sync request.
* The time spent parsing the response from an initial /sync request. In
* this case, `itemCount` should contain the number of joined rooms.
*/
InitialSyncParsing,
/**
* The time spent waiting for a response to an initial /sync request.
* The time spent waiting for a response to an initial /sync request. In
* this case, `itemCount` should contain the number of joined rooms.
*/
InitialSyncRequest,
@ -62,13 +64,16 @@ data class PerformanceTimer(
NotificationsOpenEvent,
/**
* The duration of a regular /sync request when resuming the app.
* The duration of a regular /sync request when resuming the app. In
* this case, `itemCount` should contain the number of joined rooms in
* the response.
*/
StartupIncrementalSync,
/**
* The duration of an initial /sync request during startup (if the store
* has been wiped).
* has been wiped). In this case, `itemCount` should contain the number
* of joined rooms.
*/
StartupInitialSync,

View File

@ -16,6 +16,7 @@
package im.vector.app.features.call
import android.app.Activity
import android.app.KeyguardManager
import android.app.PictureInPictureParams
import android.content.Context
@ -43,6 +44,7 @@ import com.google.android.material.card.MaterialCardView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL
@ -518,13 +520,19 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
}
is VectorCallViewEvents.ShowCallTransferScreen -> {
val callId = withState(callViewModel) { it.callId }
navigator.openCallTransfer(this, callId)
navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId)
}
null -> {
}
}
}
private val callTransferActivityResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_CANCELED) {
callViewModel.handle(VectorCallViewActions.CallTransferSelectionCancelled)
}
}
private fun onErrorTimoutConnect(turn: TurnServerResponse?) {
Timber.tag(loggerTag.value).d("onErrorTimoutConnect $turn")
// TODO ask to use default stun, etc...

View File

@ -36,5 +36,6 @@ sealed class VectorCallViewActions : VectorViewModelAction {
object ToggleCamera : VectorCallViewActions()
object ToggleHDSD : VectorCallViewActions()
object InitiateCallTransfer : VectorCallViewActions()
object CallTransferSelectionCancelled : VectorCallViewActions()
object TransferCall : VectorCallViewActions()
}

View File

@ -319,10 +319,14 @@ class VectorCallViewModel @AssistedInject constructor(
call?.sendDtmfDigit(action.digit)
}
VectorCallViewActions.InitiateCallTransfer -> {
call?.updateRemoteOnHold(true)
_viewEvents.post(
VectorCallViewEvents.ShowCallTransferScreen
)
}
VectorCallViewActions.CallTransferSelectionCancelled -> {
call?.updateRemoteOnHold(false)
}
VectorCallViewActions.TransferCall -> {
handleCallTransfer()
}

View File

@ -16,6 +16,7 @@
package im.vector.app.features.call.transfer
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
@ -55,7 +56,7 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
callTransferViewModel.observeViewEvents {
when (it) {
is CallTransferViewEvents.Dismiss -> finish()
is CallTransferViewEvents.Complete -> handleComplete()
CallTransferViewEvents.Loading -> showWaitingView()
is CallTransferViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
}
@ -93,6 +94,11 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
}
}
private fun handleComplete() {
setResult(Activity.RESULT_OK)
finish()
}
companion object {
fun newIntent(context: Context, callId: String): Intent {

View File

@ -19,7 +19,7 @@ package im.vector.app.features.call.transfer
import im.vector.app.core.platform.VectorViewEvents
sealed class CallTransferViewEvents : VectorViewEvents {
object Dismiss : CallTransferViewEvents()
object Complete : CallTransferViewEvents()
object Loading : CallTransferViewEvents()
object FailToTransfer : CallTransferViewEvents()
}

View File

@ -50,14 +50,14 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState:
private val callListener = object : WebRtcCall.Listener {
override fun onStateUpdate(call: MxCall) {
if (call.state is CallState.Ended) {
_viewEvents.post(CallTransferViewEvents.Dismiss)
_viewEvents.post(CallTransferViewEvents.Complete)
}
}
}
init {
if (call == null) {
_viewEvents.post(CallTransferViewEvents.Dismiss)
_viewEvents.post(CallTransferViewEvents.Complete)
} else {
call.addListener(callListener)
}
@ -89,7 +89,7 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState:
} else {
call?.transferToUser(action.selectedUserId, null)
}
_viewEvents.post(CallTransferViewEvents.Dismiss)
_viewEvents.post(CallTransferViewEvents.Complete)
} catch (failure: Throwable) {
_viewEvents.post(CallTransferViewEvents.FailToTransfer)
}
@ -111,7 +111,7 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState:
} else {
call?.transferToUser(result.userId, result.roomId)
}
_viewEvents.post(CallTransferViewEvents.Dismiss)
_viewEvents.post(CallTransferViewEvents.Complete)
} catch (failure: Throwable) {
_viewEvents.post(CallTransferViewEvents.FailToTransfer)
}

View File

@ -20,7 +20,6 @@ import android.net.Uri
import android.view.View
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.call.conference.ConferenceEvent
import im.vector.app.features.location.LocationData
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
@ -90,6 +89,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class EnsureNativeWidgetAllowed(val widget: Widget,
val userJustAccepted: Boolean,
val grantedEvents: RoomDetailViewEvents) : RoomDetailAction()
data class UpdateJoinJitsiCallStatus(val conferenceEvent: ConferenceEvent) : RoomDetailAction()
data class OpenOrCreateDm(val userId: String) : RoomDetailAction()
@ -112,7 +112,4 @@ sealed class RoomDetailAction : VectorViewModelAction {
// Poll
data class EndPoll(val eventId: String) : RoomDetailAction()
// Location
data class ShowLocation(val locationData: LocationData, val userId: String) : RoomDetailAction()
}

View File

@ -171,8 +171,8 @@ import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillImageSpan
import im.vector.app.features.html.PillsPostProcessor
import im.vector.app.features.invite.VectorInviteView
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.LocationSharingMode
import im.vector.app.features.location.toLocationData
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
import im.vector.app.features.notifications.NotificationDrawerManager
@ -481,7 +481,6 @@ class RoomDetailFragment @Inject constructor(
RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects()
is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it)
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
is RoomDetailViewEvents.ShowLocation -> handleShowLocationPreview(it)
}.exhaustive
}
@ -613,14 +612,14 @@ class RoomDetailFragment @Inject constructor(
}
}
private fun handleShowLocationPreview(viewEvent: RoomDetailViewEvents.ShowLocation) {
private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) {
navigator
.openLocationSharing(
context = requireContext(),
roomId = roomDetailArgs.roomId,
mode = LocationSharingMode.PREVIEW,
initialLocationData = viewEvent.locationData,
locationOwnerId = viewEvent.userId
initialLocationData = locationContent.toLocationData(),
locationOwnerId = senderId
)
}
@ -1828,6 +1827,12 @@ class RoomDetailFragment @Inject constructor(
is EncryptedEventContent -> {
roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId))
}
is MessageLocationContent -> {
handleShowLocationPreview(messageContent, informationData.senderId)
}
else -> {
Timber.d("No click action defined for this message content")
}
}
}
@ -1940,7 +1945,7 @@ class RoomDetailFragment @Inject constructor(
when (action.messageContent) {
is MessageTextContent -> shareText(requireContext(), action.messageContent.body)
is MessageLocationContent -> {
LocationData.create(action.messageContent.getUri())?.let {
action.messageContent.toLocationData()?.let {
openLocation(requireActivity(), it.latitude, it.longitude)
}
}

View File

@ -20,7 +20,6 @@ import android.net.Uri
import android.view.View
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.location.LocationData
import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
@ -83,6 +82,4 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents()
object StopChatEffects : RoomDetailViewEvents()
object RoomReplacementStarted : RoomDetailViewEvents()
data class ShowLocation(val locationData: LocationData, val userId: String) : RoomDetailViewEvents()
}

View File

@ -53,7 +53,6 @@ import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandle
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.typing.TypingHelper
import im.vector.app.features.location.LocationData
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorDataStore
@ -385,14 +384,9 @@ class RoomDetailViewModel @AssistedInject constructor(
_viewEvents.post(RoomDetailViewEvents.OpenRoom(action.replacementRoomId, closeCurrentRoom = true))
}
is RoomDetailAction.EndPoll -> handleEndPoll(action.eventId)
is RoomDetailAction.ShowLocation -> handleShowLocation(action.locationData, action.userId)
}.exhaustive
}
private fun handleShowLocation(locationData: LocationData, userId: String) {
_viewEvents.post(RoomDetailViewEvents.ShowLocation(locationData, userId))
}
private fun handleJitsiCallJoinStatus(action: RoomDetailAction.UpdateJoinJitsiCallStatus) = withState { state ->
if (state.jitsiState.confId == null) {
// If jitsi widget is removed while on the call

View File

@ -39,7 +39,9 @@ import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod
import im.vector.app.features.home.room.detail.timeline.tools.linkify
import im.vector.app.features.html.SpanUtils
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
import im.vector.app.features.location.UrlMapProvider
import im.vector.app.features.location.toLocationData
import im.vector.app.features.media.ImageContentRenderer
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.extensions.orFalse
@ -62,6 +64,7 @@ class MessageActionsEpoxyController @Inject constructor(
private val spanUtils: SpanUtils,
private val eventDetailsFormatter: EventDetailsFormatter,
private val dateFormatter: VectorDateFormatter,
private val urlMapProvider: UrlMapProvider,
private val locationPinProvider: LocationPinProvider
) : TypedEpoxyController<MessageActionState>() {
@ -74,9 +77,11 @@ class MessageActionsEpoxyController @Inject constructor(
val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL)
val body = state.messageBody.linkify(host.listener)
val bindingOptions = spanUtils.getBindingOptions(body)
val locationData = state.timelineEvent()?.root?.getClearContent()?.toModel<MessageLocationContent>(catchError = true)?.let {
LocationData.create(it.getUri())
}
val locationUrl = state.timelineEvent()?.root?.getClearContent()
?.toModel<MessageLocationContent>(catchError = true)
?.toLocationData()
?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) }
bottomSheetMessagePreviewItem {
id("preview")
avatarRenderer(host.avatarRenderer)
@ -89,7 +94,7 @@ class MessageActionsEpoxyController @Inject constructor(
body(body.toEpoxyCharSequence())
bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)?.toEpoxyCharSequence())
time(formattedDate)
locationData(locationData)
locationUrl(locationUrl)
locationPinProvider(host.locationPinProvider)
}

View File

@ -16,6 +16,7 @@
package im.vector.app.features.home.room.detail.timeline.factory
import android.content.res.Resources
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
@ -33,7 +34,6 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.core.utils.containsOnlyEmojis
import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
@ -68,7 +68,9 @@ import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillsPostProcessor
import im.vector.app.features.html.SpanUtils
import im.vector.app.features.html.VectorHtmlCompressor
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
import im.vector.app.features.location.UrlMapProvider
import im.vector.app.features.location.toLocationData
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
import im.vector.app.features.settings.VectorPreferences
@ -123,7 +125,10 @@ class MessageItemFactory @Inject constructor(
private val session: Session,
private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker,
private val locationPinProvider: LocationPinProvider,
private val vectorPreferences: VectorPreferences) {
private val vectorPreferences: VectorPreferences,
private val urlMapProvider: UrlMapProvider,
private val resources: Resources
) {
// TODO inject this properly?
private var roomId: String = ""
@ -178,7 +183,7 @@ class MessageItemFactory @Inject constructor(
is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
is MessageLocationContent -> {
if (vectorPreferences.labsRenderLocationsInTimeline()) {
buildLocationItem(messageContent, informationData, highlight, callback, attributes)
buildLocationItem(messageContent, informationData, highlight, attributes)
} else {
buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
}
@ -193,27 +198,21 @@ class MessageItemFactory @Inject constructor(
private fun buildLocationItem(locationContent: MessageLocationContent,
informationData: MessageInformationData,
highlight: Boolean,
callback: TimelineEventController.Callback?,
attributes: AbsMessageItem.Attributes): MessageLocationItem? {
val geoUri = locationContent.getUri()
val locationData = LocationData.create(geoUri)
val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60)
val height = dimensionConverter.dpToPx(200)
val mapCallback: MessageLocationItem.Callback = object : MessageLocationItem.Callback {
override fun onMapClicked() {
locationData?.let {
callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(it, informationData.senderId))
}
}
val locationUrl = locationContent.toLocationData()?.let {
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
}
return MessageLocationItem_()
.attributes(attributes)
.locationData(locationData)
.locationUrl(locationUrl)
.userId(informationData.senderId)
.locationPinProvider(locationPinProvider)
.highlighted(highlight)
.leftGuideline(avatarSizeProvider.leftGuideline)
.callback(mapCallback)
}
private fun buildPollItem(pollContent: MessagePollContent,

View File

@ -28,6 +28,7 @@ import im.vector.app.core.glide.GlideApp
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -54,22 +55,36 @@ class LocationPinProvider @Inject constructor(
val size = dimensionConverter.dpToPx(44)
avatarRenderer.render(glideRequests, it, object : CustomTarget<Drawable>(size, size) {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!!
val layerDrawable = LayerDrawable(arrayOf(bgUserPin, resource))
val horizontalInset = dimensionConverter.dpToPx(4)
val topInset = dimensionConverter.dpToPx(4)
val bottomInset = dimensionConverter.dpToPx(8)
layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset)
cache[userId] = layerDrawable
callback(layerDrawable)
Timber.d("## Location: onResourceReady")
val pinDrawable = createPinDrawable(resource)
cache[userId] = pinDrawable
callback(pinDrawable)
}
override fun onLoadCleared(placeholder: Drawable?) {
// Is it possible? Put placeholder instead?
// FIXME The doc says it has to be implemented and should free resources
Timber.d("## Location: onLoadCleared")
}
override fun onLoadFailed(errorDrawable: Drawable?) {
Timber.w("## Location: onLoadFailed")
errorDrawable ?: return
val pinDrawable = createPinDrawable(errorDrawable)
cache[userId] = pinDrawable
callback(pinDrawable)
}
})
}
}
private fun createPinDrawable(drawable: Drawable): Drawable {
val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!!
val layerDrawable = LayerDrawable(arrayOf(bgUserPin, drawable))
val horizontalInset = dimensionConverter.dpToPx(4)
val topInset = dimensionConverter.dpToPx(4)
val bottomInset = dimensionConverter.dpToPx(8)
layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset)
return layerDrawable
}
}

View File

@ -41,9 +41,6 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
@EpoxyAttribute
open var leftGuideline: Int = 0
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
lateinit var dimensionConverter: DimensionConverter
final override fun getViewType(): Int {
// This makes sure we have a unique integer for the combination of layout and ViewStubId.
return pairingFunction(layout, getViewStubId()).also {

View File

@ -16,73 +16,51 @@
package im.vector.app.features.home.room.detail.timeline.item
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import android.widget.ImageView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.google.android.material.card.MaterialCardView
import com.bumptech.glide.request.RequestOptions
import im.vector.app.R
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
import im.vector.app.features.home.room.detail.timeline.style.shapeAppearanceModel
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.MapTilerMapView
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>() {
interface Callback {
fun onMapClicked()
}
@EpoxyAttribute
var callback: Callback? = null
@EpoxyAttribute
var locationData: LocationData? = null
var locationUrl: String? = null
@EpoxyAttribute
var userId: String? = null
@EpoxyAttribute
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var locationPinProvider: LocationPinProvider? = null
override fun bind(holder: Holder) {
super.bind(holder)
renderSendState(holder.mapViewContainer, null)
val location = locationData ?: return
renderSendState(holder.view, null)
val location = locationUrl ?: return
val locationOwnerId = userId ?: return
GlideApp.with(holder.staticMapImageView)
.load(location)
.apply(RequestOptions.centerCropTransform())
.into(holder.staticMapImageView)
holder.clickableMapArea.onClick {
callback?.onMapClicked()
}
holder.mapView.apply {
initialize {
zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM)
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
addPinToMap(locationOwnerId, pinDrawable)
updatePinLocation(locationOwnerId, location.latitude, location.longitude)
}
}
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
GlideApp.with(holder.staticMapPinImageView)
.load(pinDrawable)
.into(holder.staticMapPinImageView)
}
}
override fun getViewStubId() = STUB_ID
class Holder : AbsMessageItem.Holder(STUB_ID) {
val mapCardView by bind<MaterialCardView>(R.id.mapCardView)
val mapViewContainer by bind<ConstraintLayout>(R.id.mapViewContainer)
val mapView by bind<MapTilerMapView>(R.id.mapView)
val clickableMapArea by bind<FrameLayout>(R.id.clickableMapArea)
val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
}
companion object {
private const val STUB_ID = R.id.messageContentLocationStub
private const val INITIAL_ZOOM = 15.0
}
}

View File

@ -110,6 +110,7 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
val textFuture = PrecomputedTextCompat.getTextFuture(message, TextViewCompat.getTextMetricsParams(this), null)
setTextFuture(textFuture)
} else {
setTextFuture(null)
text = message
}
}

View File

@ -16,6 +16,10 @@
package im.vector.app.features.location
const val INITIAL_MAP_ZOOM = 15.0
const val MIN_TIME_MILLIS_TO_UPDATE_LOCATION = 1 * 60 * 1000L // every 1 minute
const val MIN_DISTANCE_METERS_TO_UPDATE_LOCATION = 10f
const val MAP_BASE_URL = "https://api.maptiler.com/maps/streets/style.json"
const val STATIC_MAP_BASE_URL = "https://api.maptiler.com/maps/basic/static/"
const val INITIAL_MAP_ZOOM_IN_PREVIEW = 15.0
const val INITIAL_MAP_ZOOM_IN_TIMELINE = 17.0
const val MIN_TIME_TO_UPDATE_LOCATION_MILLIS = 5 * 1_000L // every 5 seconds
const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f

View File

@ -17,41 +17,43 @@
package im.vector.app.features.location
import android.os.Parcelable
import androidx.annotation.VisibleForTesting
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
@Parcelize
data class LocationData(
val latitude: Double,
val longitude: Double,
val uncertainty: Double?
) : Parcelable {
) : Parcelable
companion object {
/**
* Creates location data from geo uri
* @param geoUri geo:latitude,longitude;uncertainty
* @return location data or null if geo uri is not valid
*/
fun create(geoUri: String): LocationData? {
val geoParts = geoUri
.split(":")
.takeIf { it.firstOrNull() == "geo" }
?.getOrNull(1)
?.split(",")
val latitude = geoParts?.firstOrNull()
val geoTailParts = geoParts?.getOrNull(1)?.split(";")
val longitude = geoTailParts?.firstOrNull()
val uncertainty = geoTailParts?.getOrNull(1)?.replace("u=", "")
return if (latitude != null && longitude != null) {
LocationData(
latitude = latitude.toDouble(),
longitude = longitude.toDouble(),
uncertainty = uncertainty?.toDouble()
)
} else null
}
}
/**
* Creates location data from a LocationContent
* "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30)
* @return location data or null if geo uri is not valid
*/
fun MessageLocationContent.toLocationData(): LocationData? {
return parseGeo(getBestGeoUri())
}
@VisibleForTesting
fun parseGeo(geo: String): LocationData? {
val geoParts = geo
.split(":")
.takeIf { it.firstOrNull() == "geo" }
?.getOrNull(1)
?.split(";") ?: return null
val gpsParts = geoParts.getOrNull(0)?.split(",") ?: return null
val lat = gpsParts.getOrNull(0)?.toDoubleOrNull() ?: return null
val lng = gpsParts.getOrNull(1)?.toDoubleOrNull() ?: return null
val uncertainty = geoParts.getOrNull(1)?.replace("u=", "")?.toDoubleOrNull()
return LocationData(
latitude = lat,
longitude = lng,
uncertainty = uncertainty
)
}

View File

@ -21,20 +21,30 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.args
import com.mapbox.mapboxsdk.maps.MapView
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.openLocation
import im.vector.app.databinding.FragmentLocationPreviewBinding
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import java.lang.ref.WeakReference
import javax.inject.Inject
/**
* TODO Move locationPinProvider to a ViewModel
*/
class LocationPreviewFragment @Inject constructor(
private val urlMapProvider: UrlMapProvider,
private val locationPinProvider: LocationPinProvider
) : VectorBaseFragment<FragmentLocationPreviewBinding>() {
private val args: LocationSharingArgs by args()
// Keep a ref to handle properly the onDestroy callback
private var mapView: WeakReference<MapView>? = null
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationPreviewBinding {
return FragmentLocationPreviewBinding.inflate(layoutInflater, container, false)
}
@ -42,11 +52,15 @@ class LocationPreviewFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
views.mapView.initialize {
if (isAdded) {
onMapReady()
}
}
mapView = WeakReference(views.mapView)
views.mapView.onCreate(savedInstanceState)
views.mapView.initialize(urlMapProvider.mapUrl)
loadPinDrawable()
}
override fun onResume() {
super.onResume()
views.mapView.onResume()
}
override fun onPause() {
@ -54,11 +68,32 @@ class LocationPreviewFragment @Inject constructor(
super.onPause()
}
override fun onLowMemory() {
views.mapView.onLowMemory()
super.onLowMemory()
}
override fun onStart() {
super.onStart()
views.mapView.onStart()
}
override fun onStop() {
views.mapView.onStop()
super.onStop()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
views.mapView.onSaveInstanceState(outState)
}
override fun onDestroy() {
mapView?.get()?.onDestroy()
mapView?.clear()
super.onDestroy()
}
override fun getMenuRes() = R.menu.menu_location_preview
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -76,18 +111,20 @@ class LocationPreviewFragment @Inject constructor(
openLocation(requireActivity(), location.latitude, location.longitude)
}
private fun onMapReady() {
if (!isAdded) return
private fun loadPinDrawable() {
val location = args.initialLocationData ?: return
val userId = args.locationOwnerId
locationPinProvider.create(userId) { pinDrawable ->
views.mapView.apply {
zoomToLocation(location.latitude, location.longitude, INITIAL_MAP_ZOOM)
deleteAllPins()
addPinToMap(userId, pinDrawable)
updatePinLocation(userId, location.latitude, location.longitude)
lifecycleScope.launchWhenResumed {
views.mapView.render(
MapState(
zoomOnlyOnce = true,
pinLocationData = location,
pinId = args.locationOwnerId,
pinDrawable = pinDrawable
)
)
}
}
}

View File

@ -19,7 +19,5 @@ package im.vector.app.features.location
import im.vector.app.core.platform.VectorViewModelAction
sealed class LocationSharingAction : VectorViewModelAction {
data class OnLocationUpdate(val locationData: LocationData) : LocationSharingAction()
object OnShareLocation : LocationSharingAction()
object OnLocationProviderIsNotAvailable : LocationSharingAction()
}

View File

@ -20,29 +20,29 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isGone
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mapbox.mapboxsdk.maps.MapView
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentLocationSharingBinding
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import org.matrix.android.sdk.api.session.Session
import java.lang.ref.WeakReference
import javax.inject.Inject
/**
* We should consider using SupportMapFragment for a out of the box lifecycle handling
*/
class LocationSharingFragment @Inject constructor(
private val locationTracker: LocationTracker,
private val session: Session,
private val locationPinProvider: LocationPinProvider
) : VectorBaseFragment<FragmentLocationSharingBinding>(), LocationTracker.Callback {
init {
locationTracker.callback = this
}
private val urlMapProvider: UrlMapProvider
) : VectorBaseFragment<FragmentLocationSharingBinding>() {
private val viewModel: LocationSharingViewModel by fragmentViewModel()
private var lastZoomValue: Double = -1.0
// Keep a ref to handle properly the onDestroy callback
private var mapView: WeakReference<MapView>? = null
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding {
return FragmentLocationSharingBinding.inflate(inflater, container, false)
@ -51,11 +51,9 @@ class LocationSharingFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
views.mapView.initialize {
if (isAdded) {
onMapReady()
}
}
mapView = WeakReference(views.mapView)
views.mapView.onCreate(savedInstanceState)
views.mapView.initialize(urlMapProvider.mapUrl)
views.shareLocationContainer.debouncedClicks {
viewModel.handle(LocationSharingAction.OnShareLocation)
@ -63,54 +61,48 @@ class LocationSharingFragment @Inject constructor(
viewModel.observeViewEvents {
when (it) {
LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError()
LocationSharingViewEvents.Close -> activity?.finish()
LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError()
LocationSharingViewEvents.Close -> activity?.finish()
}.exhaustive
}
}
override fun onResume() {
super.onResume()
views.mapView.onResume()
}
override fun onPause() {
views.mapView.onPause()
super.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
views.mapView.onSaveInstanceState(outState)
}
override fun onStart() {
super.onStart()
views.mapView.onStart()
}
override fun onStop() {
views.mapView.onStop()
super.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
views.mapView.onLowMemory()
}
override fun onDestroy() {
locationTracker.stop()
mapView?.get()?.onDestroy()
mapView?.clear()
super.onDestroy()
}
private fun onMapReady() {
if (!isAdded) return
locationPinProvider.create(session.myUserId) {
views.mapView.addPinToMap(
pinId = USER_PIN_NAME,
image = it,
)
// All set, start location tracker
locationTracker.start()
}
}
override fun onLocationUpdate(locationData: LocationData) {
lastZoomValue = if (lastZoomValue == -1.0) INITIAL_MAP_ZOOM else views.mapView.getCurrentZoom() ?: INITIAL_MAP_ZOOM
views.mapView.zoomToLocation(locationData.latitude, locationData.longitude, lastZoomValue)
views.mapView.deleteAllPins()
views.mapView.updatePinLocation(USER_PIN_NAME, locationData.latitude, locationData.longitude)
viewModel.handle(LocationSharingAction.OnLocationUpdate(locationData))
}
override fun onLocationProviderIsNotAvailable() {
viewModel.handle(LocationSharingAction.OnLocationProviderIsNotAvailable)
}
private fun handleLocationNotAvailableError() {
MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.location_not_available_dialog_title)
@ -118,9 +110,15 @@ class LocationSharingFragment @Inject constructor(
.setPositiveButton(R.string.ok) { _, _ ->
activity?.finish()
}
.setCancelable(false)
.show()
}
override fun invalidate() = withState(viewModel) { state ->
views.mapView.render(state.toMapState())
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null
}
companion object {
const val USER_PIN_NAME = "USER_PIN_NAME"
}

View File

@ -24,12 +24,15 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import org.matrix.android.sdk.api.session.Session
class LocationSharingViewModel @AssistedInject constructor(
@Assisted private val initialState: LocationSharingViewState,
session: Session
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState) {
private val locationTracker: LocationTracker,
private val locationPinProvider: LocationPinProvider,
private val session: Session
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState), LocationTracker.Callback {
private val room = session.getRoom(initialState.roomId)!!
@ -38,14 +41,31 @@ class LocationSharingViewModel @AssistedInject constructor(
override fun create(initialState: LocationSharingViewState): LocationSharingViewModel
}
companion object : MavericksViewModelFactory<LocationSharingViewModel, LocationSharingViewState> by hiltMavericksViewModelFactory() {
companion object : MavericksViewModelFactory<LocationSharingViewModel, LocationSharingViewState> by hiltMavericksViewModelFactory()
init {
locationTracker.start(this)
createPin()
}
private fun createPin() {
locationPinProvider.create(session.myUserId) {
setState {
copy(
pinDrawable = it
)
}
}
}
override fun onCleared() {
super.onCleared()
locationTracker.stop()
}
override fun handle(action: LocationSharingAction) {
when (action) {
is LocationSharingAction.OnLocationUpdate -> handleLocationUpdate(action.locationData)
LocationSharingAction.OnShareLocation -> handleShareLocation()
LocationSharingAction.OnLocationProviderIsNotAvailable -> handleLocationProviderIsNotAvailable()
LocationSharingAction.OnShareLocation -> handleShareLocation()
}.exhaustive
}
@ -62,13 +82,13 @@ class LocationSharingViewModel @AssistedInject constructor(
}
}
private fun handleLocationUpdate(locationData: LocationData) {
override fun onLocationUpdate(locationData: LocationData) {
setState {
copy(lastKnownLocation = locationData)
}
}
private fun handleLocationProviderIsNotAvailable() {
override fun onLocationProviderIsNotAvailable() {
_viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError)
}
}

View File

@ -16,6 +16,7 @@
package im.vector.app.features.location
import android.graphics.drawable.Drawable
import androidx.annotation.StringRes
import com.airbnb.mvrx.MavericksState
import im.vector.app.R
@ -28,7 +29,8 @@ enum class LocationSharingMode(@StringRes val titleRes: Int) {
data class LocationSharingViewState(
val roomId: String,
val mode: LocationSharingMode,
val lastKnownLocation: LocationData? = null
val lastKnownLocation: LocationData? = null,
val pinDrawable: Drawable? = null
) : MavericksState {
constructor(locationSharingArgs: LocationSharingArgs) : this(
@ -36,3 +38,10 @@ data class LocationSharingViewState(
mode = locationSharingArgs.mode
)
}
fun LocationSharingViewState.toMapState() = MapState(
zoomOnlyOnce = true,
pinLocationData = lastKnownLocation,
pinId = LocationSharingFragment.USER_PIN_NAME,
pinDrawable = pinDrawable
)

View File

@ -19,70 +19,108 @@ package im.vector.app.features.location
import android.Manifest
import android.content.Context
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.annotation.RequiresPermission
import androidx.core.content.getSystemService
import androidx.core.location.LocationListenerCompat
import im.vector.app.BuildConfig
import timber.log.Timber
import javax.inject.Inject
class LocationTracker @Inject constructor(
private val context: Context
) : LocationListener {
context: Context
) : LocationListenerCompat {
private val locationManager = context.getSystemService<LocationManager>()
interface Callback {
fun onLocationUpdate(locationData: LocationData)
fun onLocationProviderIsNotAvailable()
}
private var locationManager: LocationManager? = null
var callback: Callback? = null
private var callback: Callback? = null
private var hasGpsProviderLiveLocation = false
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
fun start() {
val locationManager = context.getSystemService<LocationManager>()
fun start(callback: Callback?) {
Timber.d("## LocationTracker. start()")
hasGpsProviderLiveLocation = false
this.callback = callback
locationManager?.let {
val isGpsEnabled = it.isProviderEnabled(LocationManager.GPS_PROVIDER)
val isNetworkEnabled = it.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
val provider = when {
isGpsEnabled -> LocationManager.GPS_PROVIDER
isNetworkEnabled -> LocationManager.NETWORK_PROVIDER
else -> {
callback?.onLocationProviderIsNotAvailable()
Timber.v("## LocationTracker. There is no location provider available")
return
}
}
// Send last known location without waiting location updates
it.getLastKnownLocation(provider)?.let { lastKnownLocation ->
callback?.onLocationUpdate(lastKnownLocation.toLocationData())
}
it.requestLocationUpdates(
provider,
MIN_TIME_MILLIS_TO_UPDATE_LOCATION,
MIN_DISTANCE_METERS_TO_UPDATE_LOCATION,
this
)
} ?: run {
if (locationManager == null) {
callback?.onLocationProviderIsNotAvailable()
Timber.v("## LocationTracker. LocationManager is not available")
return
}
locationManager.allProviders
.takeIf { it.isNotEmpty() }
// Take GPS first
?.sortedByDescending { if (it == LocationManager.GPS_PROVIDER) 1 else 0 }
?.forEach { provider ->
Timber.d("## LocationTracker. track location using $provider")
// Send last known location without waiting location updates
locationManager.getLastKnownLocation(provider)?.let { lastKnownLocation ->
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.d("## LocationTracker. lastKnownLocation: $lastKnownLocation")
} else {
Timber.d("## LocationTracker. lastKnownLocation: ${lastKnownLocation.provider}")
}
notifyLocation(lastKnownLocation, isLive = false)
}
locationManager.requestLocationUpdates(
provider,
MIN_TIME_TO_UPDATE_LOCATION_MILLIS,
MIN_DISTANCE_TO_UPDATE_LOCATION_METERS,
this
)
}
?: run {
callback?.onLocationProviderIsNotAvailable()
Timber.v("## LocationTracker. There is no location provider available")
}
}
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
fun stop() {
Timber.d("## LocationTracker. stop()")
locationManager?.removeUpdates(this)
callback = null
}
override fun onLocationChanged(location: Location) {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.d("## LocationTracker. onLocationChanged: $location")
} else {
Timber.d("## LocationTracker. onLocationChanged: ${location.provider}")
}
notifyLocation(location, isLive = true)
}
private fun notifyLocation(location: Location, isLive: Boolean) {
when (location.provider) {
LocationManager.GPS_PROVIDER -> {
hasGpsProviderLiveLocation = isLive
}
else -> {
if (hasGpsProviderLiveLocation) {
// Ignore this update
Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps live location")
return
}
}
}
callback?.onLocationUpdate(location.toLocationData())
}
override fun onProviderDisabled(provider: String) {
Timber.d("## LocationTracker. onProviderDisabled: $provider")
callback?.onLocationProviderIsNotAvailable()
}
private fun Location.toLocationData(): LocationData {
return LocationData(latitude, longitude, accuracy.toDouble())
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 New Vector Ltd
* Copyright (c) 2022 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.
@ -18,15 +18,9 @@ package im.vector.app.features.location
import android.graphics.drawable.Drawable
interface VectorMapView {
fun initialize(onMapReady: () -> Unit)
fun addPinToMap(pinId: String, image: Drawable)
fun updatePinLocation(pinId: String, latitude: Double, longitude: Double)
fun deleteAllPins()
fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double)
fun getCurrentZoom(): Double?
fun onClick(callback: () -> Unit)
}
data class MapState(
val zoomOnlyOnce: Boolean,
val pinLocationData: LocationData? = null,
val pinId: String,
val pinDrawable: Drawable? = null
)

View File

@ -17,7 +17,6 @@
package im.vector.app.features.location
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import com.mapbox.mapboxsdk.camera.CameraPosition
import com.mapbox.mapboxsdk.geometry.LatLng
@ -27,65 +26,76 @@ import com.mapbox.mapboxsdk.maps.Style
import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
import com.mapbox.mapboxsdk.style.layers.Property
import im.vector.app.BuildConfig
import timber.log.Timber
class MapTilerMapView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : MapView(context, attrs, defStyleAttr), VectorMapView {
) : MapView(context, attrs, defStyleAttr) {
private var map: MapboxMap? = null
private var symbolManager: SymbolManager? = null
private var style: Style? = null
private var pendingState: MapState? = null
override fun initialize(onMapReady: () -> Unit) {
data class MapRefs(
val map: MapboxMap,
val symbolManager: SymbolManager,
val style: Style
)
private var mapRefs: MapRefs? = null
private var initZoomDone = false
/**
* For location fragments
*/
fun initialize(url: String) {
Timber.d("## Location: initialize")
getMapAsync { map ->
map.setStyle(styleUrl) { style ->
this.symbolManager = SymbolManager(this, map, style)
this.map = map
this.style = style
onMapReady()
map.setStyle(url) { style ->
mapRefs = MapRefs(
map,
SymbolManager(this, map, style),
style
)
pendingState?.let { render(it) }
pendingState = null
}
}
}
override fun addPinToMap(pinId: String, image: Drawable) {
style?.addImage(pinId, image)
}
fun render(state: MapState) {
val safeMapRefs = mapRefs ?: return Unit.also {
pendingState = state
}
override fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) {
symbolManager?.create(
SymbolOptions()
.withLatLng(LatLng(latitude, longitude))
.withIconImage(pinId)
.withIconAnchor(Property.ICON_ANCHOR_BOTTOM)
)
}
state.pinDrawable?.let { pinDrawable ->
if (!safeMapRefs.style.isFullyLoaded ||
safeMapRefs.style.getImage(state.pinId) == null) {
safeMapRefs.style.addImage(state.pinId, pinDrawable)
}
}
override fun deleteAllPins() {
symbolManager?.deleteAll()
}
state.pinLocationData?.let { locationData ->
if (!initZoomDone || !state.zoomOnlyOnce) {
zoomToLocation(locationData.latitude, locationData.longitude)
initZoomDone = true
}
override fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) {
map?.cameraPosition = CameraPosition.Builder()
.target(LatLng(latitude, longitude))
.zoom(zoom)
.build()
}
override fun getCurrentZoom(): Double? {
return map?.cameraPosition?.zoom
}
override fun onClick(callback: () -> Unit) {
map?.addOnMapClickListener {
callback()
true
safeMapRefs.symbolManager.deleteAll()
safeMapRefs.symbolManager.create(
SymbolOptions()
.withLatLng(LatLng(locationData.latitude, locationData.longitude))
.withIconImage(state.pinId)
.withIconAnchor(Property.ICON_ANCHOR_BOTTOM)
)
}
}
companion object {
private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}"
private fun zoomToLocation(latitude: Double, longitude: Double) {
Timber.d("## Location: zoomToLocation")
mapRefs?.map?.cameraPosition = CameraPosition.Builder()
.target(LatLng(latitude, longitude))
.zoom(INITIAL_MAP_ZOOM_IN_PREVIEW)
.build()
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 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.location
import android.content.res.Resources
import im.vector.app.BuildConfig
import im.vector.app.R
import javax.inject.Inject
class UrlMapProvider @Inject constructor(
private val resources: Resources
) {
private val keyParam = "?key=${BuildConfig.mapTilerKey}"
// This is static so no need for a fun
val mapUrl = buildString {
append(MAP_BASE_URL)
append(keyParam)
}
fun buildStaticMapUrl(locationData: LocationData,
zoom: Double,
width: Int,
height: Int): String {
return buildString {
append(STATIC_MAP_BASE_URL)
append(locationData.longitude)
append(",")
append(locationData.latitude)
append(",")
append(zoom)
append("/")
append(width)
append("x")
append(height)
append(".png")
append(keyParam)
if (!resources.getBoolean(R.bool.is_rtl)) {
// On LTR languages we want the legal mentions to be displayed on the bottom left of the image
append("&attribution=bottomleft")
}
}
}
}

View File

@ -524,9 +524,13 @@ class DefaultNavigator @Inject constructor(
context.startActivity(RoomDevToolActivity.intent(context, roomId))
}
override fun openCallTransfer(context: Context, callId: String) {
override fun openCallTransfer(
context: Context,
activityResultLauncher: ActivityResultLauncher<Intent>,
callId: String
) {
val intent = CallTransferActivity.newIntent(context, callId)
context.startActivity(intent)
activityResultLauncher.launch(intent)
}
override fun openCreatePoll(context: Context, roomId: String, editedEventId: String?, mode: PollMode) {

View File

@ -149,7 +149,11 @@ interface Navigator {
fun openDevTools(context: Context, roomId: String)
fun openCallTransfer(context: Context, callId: String)
fun openCallTransfer(
context: Context,
activityResultLauncher: ActivityResultLauncher<Intent>,
callId: String
)
fun openCreatePoll(context: Context, roomId: String, editedEventId: String?, mode: PollMode)

View File

@ -20,6 +20,7 @@ import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.notifications.ProcessedEvent.Type.KEEP
import im.vector.app.features.notifications.ProcessedEvent.Type.REMOVE
import org.matrix.android.sdk.api.session.events.model.EventType
import timber.log.Timber
import javax.inject.Inject
private typealias ProcessedEvents = List<ProcessedEvent<NotifiableEvent>>
@ -33,9 +34,13 @@ class NotifiableEventProcessor @Inject constructor(
val processedEvents = queuedEvents.map {
val type = when (it) {
is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) REMOVE else KEEP
is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) || outdatedDetector.isMessageOutdated(it)) {
REMOVE
} else KEEP
is NotifiableMessageEvent -> when {
shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) -> REMOVE
.also { Timber.d("notification message removed due to currently viewing the same room") }
outdatedDetector.isMessageOutdated(it) -> REMOVE
.also { Timber.d("notification message removed due to being read") }
else -> KEEP
}
is SimpleNotifiableEvent -> when (it.type) {
EventType.REDACTION -> REMOVE
else -> KEEP

View File

@ -55,12 +55,12 @@ class PushRuleTriggerListener @Inject constructor(
private suspend fun createNotifiableEvents(pushEvents: PushEvents, session: Session): List<NotifiableEvent> {
return pushEvents.matchedEvents.mapNotNull { (event, pushRule) ->
Timber.v("Push rule match for event ${event.eventId}")
Timber.d("Push rule match for event ${event.eventId}")
val action = pushRule.getActions().toNotificationAction()
if (action.shouldNotify) {
resolver.resolveEvent(event, session, isNoisy = !action.soundName.isNullOrBlank())
} else {
Timber.v("Matched push rule is set to not notify")
Timber.d("Matched push rule is set to not notify")
null
}
}

View File

@ -16,9 +16,13 @@
package im.vector.app.features.onboarding
enum class FtueUseCase {
FRIENDS_FAMILY,
TEAMS,
COMMUNITIES,
SKIP
enum class FtueUseCase(val persistableValue: String) {
FRIENDS_FAMILY("friends_family"),
TEAMS("teams"),
COMMUNITIES("communities"),
SKIP("skip");
companion object {
fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue }
}
}

View File

@ -32,10 +32,14 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.configureAndStart
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.vectorStore
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ensureTrailingSlash
import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.extensions.toTrackingValue
import im.vector.app.features.analytics.plan.Identity
import im.vector.app.features.login.HomeServerConnectionConfigFactory
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.login.LoginMode
@ -73,7 +77,8 @@ class OnboardingViewModel @AssistedInject constructor(
private val reAuthHelper: ReAuthHelper,
private val stringProvider: StringProvider,
private val homeServerHistoryService: HomeServerHistoryService,
private val vectorFeatures: VectorFeatures
private val vectorFeatures: VectorFeatures,
private val analyticsTracker: AnalyticsTracker
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
@AssistedFactory
@ -125,7 +130,7 @@ class OnboardingViewModel @AssistedInject constructor(
when (action) {
is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow)
is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow)
is OnboardingAction.UpdateUseCase -> handleUpdateUseCase()
is OnboardingAction.UpdateUseCase -> handleUpdateUseCase(action)
OnboardingAction.ResetUseCase -> resetUseCase()
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
@ -458,13 +463,15 @@ class OnboardingViewModel @AssistedInject constructor(
}
}
private fun handleUpdateUseCase() {
// TODO act on the use case selection
private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) {
setState { copy(useCase = action.useCase) }
analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()))
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
}
private fun resetUseCase() {
// TODO remove stored use case
setState { copy(useCase = null) }
analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = null))
}
private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) {
@ -745,6 +752,9 @@ class OnboardingViewModel @AssistedInject constructor(
}
private suspend fun onSessionCreated(session: Session) {
awaitState().useCase?.let { useCase ->
session.vectorStore(applicationContext).setUseCase(useCase)
}
activeSessionHolder.setActiveSession(session)
authenticationService.reset()

View File

@ -40,6 +40,8 @@ data class OnboardingViewState(
@PersistState
val serverType: ServerType = ServerType.Unknown,
@PersistState
val useCase: FtueUseCase? = null,
@PersistState
val signMode: SignMode = SignMode.Unknown,
@PersistState
val resetPasswordEmail: String? = null,

View File

@ -0,0 +1,60 @@
/*
* 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.session
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import im.vector.app.features.onboarding.FtueUseCase
import kotlinx.coroutines.flow.first
import org.matrix.android.sdk.internal.util.md5
/**
* Local storage for:
* - messaging use case (Enum/String)
*/
class VectorSessionStore constructor(
private val context: Context,
myUserId: String
) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_session_store_${myUserId.md5()}")
private val useCaseKey = stringPreferencesKey("use_case")
suspend fun readUseCase() = context.dataStore.data.first().let { preferences ->
preferences[useCaseKey]?.let { FtueUseCase.from(it) }
}
suspend fun setUseCase(useCase: FtueUseCase) {
context.dataStore.edit { settings ->
settings[useCaseKey] = useCase.persistableValue
}
}
suspend fun resetUseCase() {
context.dataStore.edit { settings ->
settings.remove(useCaseKey)
}
}
suspend fun clear() {
context.dataStore.edit { settings -> settings.clear() }
}
}

View File

@ -48,4 +48,13 @@
android:textColor="?colorPrimary"
android:textStyle="bold" />
<ProgressBar
android:id="@+id/shareLocationGpsLoading"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
app:layout_constraintBottom_toBottomOf="@id/shareLocationContainer"
app:layout_constraintEnd_toEndOf="@id/shareLocationContainer"
app:layout_constraintTop_toTopOf="@id/shareLocationContainer" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -106,18 +106,34 @@
tools:text="1080 x 1024 - 43s - 12kB"
tools:visibility="visible" />
<im.vector.app.features.location.MapTilerMapView
android:id="@+id/bottom_sheet_message_preview_location"
<com.google.android.material.card.MaterialCardView
android:id="@+id/mapViewContainer"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:contentDescription="@string/attachment_type_location"
android:scaleType="centerCrop"
android:visibility="gone"
app:cardCornerRadius="8dp"
app:layout_constraintEnd_toEndOf="@id/bottom_sheet_message_preview_timestamp"
app:layout_constraintStart_toStartOf="@id/bottom_sheet_message_preview_sender"
app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message_preview_sender"
app:mapbox_renderTextureMode="true"
tools:visibility="visible" />
tools:alpha="0.3"
tools:visibility="visible">
<ImageView
android:id="@+id/staticMapImageView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:contentDescription="@string/a11y_static_map_image" />
<ImageView
android:id="@+id/staticMapPinImageView"
android:layout_width="51dp"
android:layout_height="55dp"
android:layout_gravity="center"
android:layout_marginBottom="28dp"
android:importantForAccessibility="no"
android:src="@drawable/bg_map_user_pin" />
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,36 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapCardView"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
app:cardCornerRadius="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/mapViewContainer"
android:minWidth="@dimen/chat_bubble_fixed_size"
<ImageView
android:id="@+id/staticMapImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="200dp"
android:contentDescription="@string/a11y_static_map_image" />
<im.vector.app.features.location.MapTilerMapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="200dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:mapbox_renderTextureMode="true" />
<FrameLayout
android:id="@+id/clickableMapArea"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/mapView"
app:layout_constraintEnd_toEndOf="@id/mapView"
app:layout_constraintStart_toStartOf="@id/mapView"
app:layout_constraintTop_toTopOf="@id/mapView" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/staticMapPinImageView"
android:layout_width="51dp"
android:layout_height="55dp"
android:layout_gravity="center"
android:layout_marginBottom="28dp"
android:importantForAccessibility="no"
android:src="@drawable/bg_map_user_pin" />
</com.google.android.material.card.MaterialCardView>

View File

@ -611,7 +611,7 @@
<string name="room_participants_action_mention">Zmínit</string>
<string name="room_participants_action_devices_list">Zobrazit seznam relací</string>
<string name="room_preview_room_interactions_disabled">Toto je náhled místnosti. Interakce s místností byla vypnuta.</string>
<string name="room_participants_ban_prompt_msg">Vykázání uživatele jej vykopne z této místnosti a zamezí, aby opět vstoupili.</string>
<string name="room_participants_ban_prompt_msg">Vykázání uživatele jej odebere z této místnosti a zamezí, aby opět vstoupil.</string>
<string name="reason_hint">Důvod</string>
<string name="room_participants_invite_prompt_msg">Opravdu chcete pozvat uživatele %s do této konverzace\?</string>
<string name="room_participants_invite_join_names">"%1$s, "</string>
@ -930,7 +930,7 @@
<string name="settings_show_read_receipts">Ukázat potvrzení o přečtení</string>
<string name="settings_show_read_receipts_summary">Klepněte na potvrzení o přečtení pro podrobnosti.</string>
<string name="settings_show_join_leave_messages">Ukázat události příchodů a odchodů</string>
<string name="settings_show_join_leave_messages_summary">Nemá vliv na pozvánky, nakopnutí a zákazy.</string>
<string name="settings_show_join_leave_messages_summary">Pozvánky, odebrání a vykázání nejsou dotčeny.</string>
<string name="settings_show_avatar_display_name_changes_messages">Ukázat události účtu</string>
<string name="settings_show_avatar_display_name_changes_messages_summary">Zahrnout avatar a změny veřejného jména.</string>
<string name="settings_vibrate_on_mention">Vibrovat při zmínce uživatele</string>
@ -2386,7 +2386,7 @@
<item quantity="other">%d vteřin</item>
</plurals>
<string name="settings_show_room_member_state_events">Zobrazit stavové události účastníků v místnosti</string>
<string name="settings_show_room_member_state_events_summary">Zahrnuje události pozvat/vstoupit/opustit/vykopnout/vykázat a změny avatara/veřejného jména.</string>
<string name="settings_show_room_member_state_events_summary">Zahrnuje události pozvat/vstoupit/opustit/odebrat/vykázat a změny avatara/veřejného jména.</string>
<string name="sent_a_poll">Hlasování</string>
<string name="sent_a_reaction">Reagoval(a): %s</string>
<string name="sent_verification_conclusion">Výsledek ověření</string>
@ -3010,7 +3010,7 @@
<string name="space_settings_permissions_subtitle">Zobrazit a aktualizovat role potřebné ke změně různých částí prostoru.</string>
<string name="space_settings_permissions_title">Oprávnění prostoru</string>
<string name="space_participants_unban_prompt_msg">Zrušením vykázání uživateli umožní znovu se připojit do prostoru.</string>
<string name="space_participants_ban_prompt_msg">Vykázáním uživatele z tohoto prostoru vykopnete a zabráníte mu v dalším připojení.</string>
<string name="space_participants_ban_prompt_msg">Vykázání uživatele z tohoto prostoru ho odebere a zamezí, aby opět vstoupil.</string>
<string name="space_participants_remove_prompt_msg">vykopnutí uživatele je z tohoto prostoru odstraní.
\n
\nAbyste jim zabránili v dalším vstupu, měli byste je raději vykázat.</string>
@ -3072,8 +3072,8 @@
<item quantity="other">Konečný výsledek na základě %1$d hlasů</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
<item quantity="one">Nikdo nehlasoval</item>
<item quantity="few">%1$d hlasů. Hlasujte pro zobrazení výsledků</item>
<item quantity="one">%1$d hlas. Hlasujte pro zobrazení výsledků</item>
<item quantity="few">%1$d hlasy. Hlasujte pro zobrazení výsledků</item>
<item quantity="other">%1$d hlasů. Hlasujte pro zobrazení výsledků</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_voted">
@ -3120,7 +3120,40 @@
<string name="room_member_override_nick_color">Přepsat barvu přezdívky</string>
<string name="login_splash_already_have_account">Již mám účet</string>
<string name="ftue_auth_carousel_4_title">Zlepšete týmovou komunikaci.</string>
<string name="ftue_auth_carousel_3_title">Spojte se s kýmkoli.</string>
<string name="ftue_auth_carousel_3_title">Bezpečené zasílání zpráv.</string>
<string name="ftue_auth_carousel_2_title">Máte vše pod kontrolou.</string>
<string name="ftue_auth_carousel_1_title">Vlastněte své konverzace.</string>
<string name="tooltip_attachment_location">Sdílet polohu</string>
<string name="labs_render_locations_in_timeline">Zobrazit polohy uživatele na časové ose</string>
<string name="settings_enable_location_sharing_summary">Po zapnutí budete moci odeslat svou polohu do libovolné místnosti</string>
<string name="settings_enable_location_sharing">Povolit sdílení polohy</string>
<string name="location_share_external">Otevřít v</string>
<string name="template_location_not_available_dialog_content">${app_name} nemohl získat přístup k vaší poloze. Zkuste to prosím později.</string>
<string name="template_location_not_available_dialog_title">${app_name} nemohl získat přístup k vaší poloze</string>
<string name="location_share">Sdílet polohu</string>
<string name="a11y_location_share_icon">Sdílet polohu</string>
<string name="location_activity_title_preview">Poloha</string>
<string name="location_activity_title_static_sharing">Sdílet polohu</string>
<string name="closed_poll_option_description">Výsledky se zobrazí až po ukončení hlasování</string>
<string name="closed_poll_option_title">Uzavřené hlasování</string>
<string name="open_poll_option_description">Hlasující vidí výsledky ihned po hlasování</string>
<string name="open_poll_option_title">Otevřené hlasování</string>
<string name="poll_type_title">Typ hlasování</string>
<string name="edit_poll_button">UPRAVIT HLASOVÁNÍ</string>
<string name="edit_poll_title">Upravit hlasování</string>
<string name="poll_no_votes_cast">Nikdo nehlasoval</string>
<string name="a11y_trust_level_misconfigured">Chybně nakonfigurovaná úroveň důvěryhodnosti</string>
<string name="encryption_misconfigured">Šifrování je špatně nakonfigurováno</string>
<string name="room_profile_section_restore_security">Obnovit šifrování</string>
<string name="room_unsupported_e2e_algorithm">Šifrování bylo špatně nakonfigurováno, takže nelze odesílat zprávy. Kontaktujte správce, aby obnovil šifrování do funkčního stavu.</string>
<string name="contact_admin_to_restore_encryption">Kontaktujte správce, aby obnovil šifrování do funkčního stavu.</string>
<string name="encryption_has_been_misconfigured">Šifrování bylo špatně nakonfigurováno.</string>
<string name="sent_location">Sdíleli svou polohu</string>
<string name="login_splash_create_account">Vytvořit účet</string>
<string name="ftue_auth_carousel_title_messaging">Zasílání zpráv pro váš tým.</string>
<string name="ftue_auth_carousel_body_encrypted">Koncové šifrování bez potřeby telefonního čísla. Žádné reklamy ani vytěžování dat.</string>
<string name="ftue_auth_carousel_body_control">Můžete si vybrat, kde budou vaše konverzace uloženy, a získat tak kontrolu a nezávislost. Připojeno přes Matrix.</string>
<string name="ftue_auth_carousel_body_secure">Bezpečná a nezávislá komunikace, která vám poskytne stejnou úroveň soukromí jako osobní rozhovor u vás doma.</string>
<string name="attachment_type_location">Poloha</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Šifrování bylo špatně nakonfigurováno, takže nelze odesílat zprávy. Kliknutím otevřete nastavení.</string>
</resources>

View File

@ -3058,4 +3058,37 @@
<item quantity="other">Endgültiges Ergebnis basiert auf %1$d Stimmen</item>
</plurals>
<string name="ftue_auth_carousel_4_title">Element verbindet Datenschutz mit tollen Features.</string>
<string name="room_profile_section_restore_security">Verschlüsselung wiederherstellen</string>
<string name="tooltip_attachment_location">Standort freigeben</string>
<string name="location_share">Standort freigeben</string>
<string name="a11y_location_share_icon">Standort freigeben</string>
<string name="location_activity_title_static_sharing">Standort freigeben</string>
<string name="settings_enable_location_sharing">Standortfreigabe aktivieren</string>
<string name="location_share_external">Öffnen mit</string>
<string name="template_location_not_available_dialog_content">${app_name} konnte nicht auf deinen Standort zugreifen. Bitte versuche es später noch einmal.</string>
<string name="template_location_not_available_dialog_title">${app_name} konnte nicht auf deinen Standort zugreifen</string>
<string name="location_activity_title_preview">Standort</string>
<string name="closed_poll_option_description">Ergebnisse werden erst angezeigt, wenn du die Umfrage beendest</string>
<string name="closed_poll_option_title">Geschlossene Umfrage</string>
<string name="open_poll_option_description">Ergebnisse werden direkt nach Stimmabgabe angezeigt</string>
<string name="open_poll_option_title">Offene Umfrage</string>
<string name="poll_type_title">Umfragetyp</string>
<string name="edit_poll_button">UMFRAGE BEARBEITEN</string>
<string name="edit_poll_title">Umfrage bearbeiten</string>
<string name="poll_no_votes_cast">Keine Stimmen abgegeben</string>
<string name="login_splash_create_account">Konto erstellen</string>
<string name="ftue_auth_carousel_title_messaging">Nachrichtenaustausch für dein Team.</string>
<string name="ftue_auth_carousel_body_encrypted">Ende-zu-Ende-verschlüsselt und ohne Telefonnummer nutzbar. Keine Werbung oder Datenerfassung.</string>
<string name="ftue_auth_carousel_body_control">Wähle wo deine Gespräche liegen, für Kontrolle und Unabhängigkeit. Verbunden mit Matrix.</string>
<string name="ftue_auth_carousel_body_secure">Sichere und unabhängige Kommunikation, die für die gleiche Vertraulichkeit sorgt, wie ein Gespräch von Angesicht zu Angesicht in deinem eigenen Zuhause.</string>
<string name="attachment_type_location">Standort</string>
<string name="a11y_trust_level_misconfigured">Fehlerhaft konfiguriertes Vertrauenslevel</string>
<string name="encryption_misconfigured">Die Verschlüsselung ist fehlerhaft konfiguriert</string>
<string name="contact_admin_to_restore_encryption">Bitte kontaktiere einen Admin, um die Verschlüsselung zurückzusetzen.</string>
<string name="encryption_has_been_misconfigured">Die Verschlüsselung wurde fehlerhaft konfiguriert.</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Die Verschlüsselung wurde fehlerhaft konfiguriert und du kannst keine Nachrichten senden. Klicke, um die Einstellungen zu öffnen.</string>
<string name="room_unsupported_e2e_algorithm">Die Verschlüsselung wurde fehlerhaft konfiguriert und du kannst keine Nachrichten senden. Bitte kontaktiere einen Admin, um die Verschlüsselung wiederherzustellen.</string>
<string name="labs_render_locations_in_timeline">Geteilte Standorte anzeigen</string>
<string name="settings_enable_location_sharing_summary">Sobald aktiviert, kannst du deinen Standort in jeden Raum senden</string>
<string name="sent_location">Hat den Standort geteilt</string>
</resources>

View File

@ -1381,7 +1381,7 @@
<string name="settings_always_show_timestamps">Näita kõikide sõnumite ajatempleid</string>
<string name="settings_show_read_receipts_summary">Lugemisteatisele klõpsimine kuvab detailsema loendi.</string>
<string name="settings_show_join_leave_messages">Näita jututubadega liitumised ja neist lahkumised</string>
<string name="settings_show_join_leave_messages_summary">Siia alla ei kuulu kutsed, müksamised ja suhtluskeelud.</string>
<string name="settings_show_join_leave_messages_summary">Siia alla ei kuulu kutsed, eemaldamised ja suhtluskeelud.</string>
<string name="settings_show_avatar_display_name_changes_messages">Näita kontoga seotud sündmusi</string>
<string name="settings_show_avatar_display_name_changes_messages_summary">Sealhulgas tunnuspildi ja kuvatava nime muutusi.</string>
<string name="settings_vibrate_on_mention">Kasutaja mainimisel anna väringa märku</string>
@ -2342,7 +2342,7 @@
<item quantity="other">%d sekundit</item>
</plurals>
<string name="settings_show_room_member_state_events">Näita jututoa liikmete olekusündmusi</string>
<string name="settings_show_room_member_state_events_summary">Sealhulgas kutsumisi, liitumisi, lahkumisi, müksamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi.</string>
<string name="settings_show_room_member_state_events_summary">Sealhulgas kutsumisi, liitumisi, lahkumisi, eemaldamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi.</string>
<string name="sent_a_poll">Küsitlus</string>
<string name="sent_a_reaction">Reageeris: %s</string>
<string name="sent_verification_conclusion">Verifitseerimise tulemus</string>
@ -3061,7 +3061,40 @@
<string name="room_member_override_nick_color">Asenda hüüdnime värvid</string>
<string name="login_splash_already_have_account">Mul on kasutajakonto juba olemas</string>
<string name="ftue_auth_carousel_4_title">Lase tiimidel vabalt tegutseda.</string>
<string name="ftue_auth_carousel_3_title">Suhtle kellega soovid.</string>
<string name="ftue_auth_carousel_3_title">Turvaline sõnumivahetus.</string>
<string name="ftue_auth_carousel_2_title">Sul on kontroll oma andmete üle.</string>
<string name="ftue_auth_carousel_1_title">Vestlused, mida sa tegelikult ka omad.</string>
<string name="tooltip_attachment_location">Jaga asukohta</string>
<string name="labs_render_locations_in_timeline">Kuva ajajoonel kasutaja asukohti</string>
<string name="settings_enable_location_sharing_summary">Kui see seadistus on kasutusel, siis sa saad oma asukohta jagada igas jututoas</string>
<string name="settings_enable_location_sharing">Luba asukohta jagada</string>
<string name="location_share_external">Ava muu rakendusega</string>
<string name="template_location_not_available_dialog_content">${app_name} ei saanud asukohta tuvastada. Palun proovi hiljem uuesti.</string>
<string name="template_location_not_available_dialog_title">${app_name} ei saanud asukohta tuvastada</string>
<string name="location_share">Jaga asukohta</string>
<string name="a11y_location_share_icon">Jaga asukohta</string>
<string name="location_activity_title_preview">Asukoht</string>
<string name="location_activity_title_static_sharing">Jaga asukohta</string>
<string name="closed_poll_option_description">Tulemusi kuvame vaid siis, kui küsitlus on lõppenud</string>
<string name="closed_poll_option_title">Küsitlus on lõppenud</string>
<string name="open_poll_option_description">Osalejad näevad tulemusi peale oma valiku salvestamist</string>
<string name="open_poll_option_title">Ava küsitlus</string>
<string name="poll_type_title">Küsitluse tüüp</string>
<string name="edit_poll_button">MUUDA KÜSITLUST</string>
<string name="edit_poll_title">Muuda küsitlust</string>
<string name="poll_no_votes_cast">Hääletanuid ei ole</string>
<string name="a11y_trust_level_misconfigured">Seadmete usaldusseosed on valesti seadistatud</string>
<string name="encryption_misconfigured">Krüptimise seadistustes on viga</string>
<string name="room_profile_section_restore_security">Taasta krüptimine</string>
<string name="contact_admin_to_restore_encryption">Kui soovid krüptimist töökorda saada, siis võta ühendust serveri haldajaga.</string>
<string name="encryption_has_been_misconfigured">Krüptimise seadistustes on viga.</string>
<string name="sent_location">Jagas oma asukohta</string>
<string name="login_splash_create_account">Loo kasutajakonto</string>
<string name="ftue_auth_carousel_title_messaging">Sõnumisuhtlus sinu tiimi või kogukonna jaoks.</string>
<string name="ftue_auth_carousel_body_encrypted">Tagatud on andmete läbiv krüptimine ning oma telefoninumbrit ei pea sa jagama. Pole reklaame ega sinu andmete kogumist.</string>
<string name="ftue_auth_carousel_body_control">Sa ise valid serveri, kus sinu vestlusi hoitakse ning sellega tagadki kontrolli oma andmete üle. Lahendus põhineb Matrix\'i võrgul.</string>
<string name="ftue_auth_carousel_body_secure">Turvaline ja sõltumatu suhtluslahendus, mis tagab sama privaatsuse, kui omavaheline vestlus sinu kodus.</string>
<string name="attachment_type_location">Asukoht</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Krüptimise seadistustes on viga ja sa ei saa sõnumeid saata. Seadistuste avamiseks klõpsi siin.</string>
<string name="room_unsupported_e2e_algorithm">Krüptimise seadistustes on viga ja sa ei saa sõnumeid saata. Kui soovid krüptimist töökorda saada, siis võta ühendust serveri haldajaga.</string>
</resources>

View File

@ -815,7 +815,7 @@
<string name="room_participants_action_unignore">لغو نادیده‌گیری</string>
<string name="room_participants_action_mention">اشاره</string>
<string name="room_participants_action_devices_list">نمایش فهرست نشست</string>
<string name="room_participants_ban_prompt_msg">انسداد کاربر، او را از این اتاق اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند.</string>
<string name="room_participants_ban_prompt_msg">انسداد کاربر، او را از این اتاق برداشته و از پیوستن دوباره‌اش جلوگیری می‌کند.</string>
<string name="reason_hint">دلیل</string>
<string name="room_details_settings">تنظیمات</string>
<string name="tab_title_search_messages">پیام‌ها</string>
@ -1002,7 +1002,7 @@
\nمدیرهای یکپارچگی، داده‌های پیکربندی را دریافت کرده و می‌توانند از طرف شما ابزارک‌ها را تغییر داده، دعوت‌های اتاق فرستاده و سطوح قدرت را تنظیم کنند.</string>
<string name="settings_always_show_timestamps">نمایش برچسب زمانی برای تمامی پیام‌ها</string>
<string name="settings_12_24_timestamps">نمایش برچسب‌های زمانی در قالب ۱۲ساعته</string>
<string name="settings_show_room_member_state_events_summary">شامل رویدادهای دعوت/پیوستن/ترک/اخراج/تحریم و تغییرهای چهرک/نام نمایشی.</string>
<string name="settings_show_room_member_state_events_summary">شامل رویدادهای دعوت/پیوستن/ترک/برداشتن/تحریم و تغییرهای چهرک/نام نمایشی.</string>
<string name="settings_secure_backup_section_title">پشتیبان امن</string>
<string name="settings_secure_backup_manage">مدیریت</string>
<string name="settings_secure_backup_setup">برپایی پشتیبان امن</string>
@ -2956,7 +2956,7 @@
<string name="space_settings_permissions_subtitle">دیدن و به‌روز رسانی نقش‌های لازم برای تغییر بخش‌های مختلف فضا.</string>
<string name="space_settings_permissions_title">اجازه‌های فضا</string>
<string name="space_participants_unban_prompt_msg">لغو تحریم کاربر، اجازهٔ پیوستن دوباره‌اش به فضا را می‌دهد.</string>
<string name="space_participants_ban_prompt_msg">تحریم کاربر، او را از این فضا اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند.</string>
<string name="space_participants_ban_prompt_msg">تحریم کاربر، او را از این فضا برداشته و از پیوستن دوباره‌اش جلوگیری می‌کند.</string>
<string name="space_participants_remove_prompt_msg">اخراج کاربر، از این فضا برش می‌دارد.
\n
\nبرای پیش‌گیری از پیوستن دوباره، باید تحریمش کنید.</string>
@ -3059,7 +3059,25 @@
<string name="labs_auto_report_uisi_desc">سامانه‌تان هنگام‌ مواجهه با خطای ناتوانی در رمزگشایی، گزارش‌ها را به صورت خودکار خواهد فرستاد</string>
<string name="labs_auto_report_uisi">گزارش خودکار خطاهای رمزگشایی.</string>
<string name="login_splash_already_have_account">از پیش حساب دارم</string>
<string name="ftue_auth_carousel_3_title">ارتباط با هرکسی.</string>
<string name="ftue_auth_carousel_3_title">پیام‌رسانی امن.</string>
<string name="ftue_auth_carousel_2_title">شما کنترل می‌کنید.</string>
<string name="ftue_auth_carousel_1_title">صاحب گفت‌وگوهایتان باشید.</string>
<string name="tooltip_attachment_location">هم‌رسانی مکان</string>
<string name="settings_enable_location_sharing">به کار انداختن هم‌رسانی مکان</string>
<string name="location_share_external">گشودن با</string>
<string name="location_share">هم‌رسانی مکان</string>
<string name="a11y_location_share_icon">هم‌رسانی مکان</string>
<string name="location_activity_title_preview">مکان</string>
<string name="location_activity_title_static_sharing">هم‌رسانی مکان</string>
<string name="closed_poll_option_title">نظرسنجی بسته</string>
<string name="open_poll_option_title">گشودن نظرسنجی</string>
<string name="poll_type_title">گونهٔ نظرسنجی</string>
<string name="edit_poll_button">ویرایش نظرسنجی</string>
<string name="edit_poll_title">ویرایش نظرسنجی</string>
<string name="poll_no_votes_cast">رأیی داده نشده</string>
<string name="room_profile_section_restore_security">بازیابی رمزنگاری</string>
<string name="sent_location">مکانش را هم‌رسانی کرد</string>
<string name="login_splash_create_account">ایجاد حساب</string>
<string name="ftue_auth_carousel_title_messaging">پیام‌رسانی برای گروهتان.</string>
<string name="attachment_type_location">مکان</string>
</resources>

View File

@ -3017,7 +3017,7 @@
<item quantity="other">Résultat final sur la base de %1$d votes</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
<item quantity="one">Aucun vote exprimé</item>
<item quantity="one">%1$d vote exprimé. Votez pour voir les résultats</item>
<item quantity="other">%1$d votes exprimés. Votez pour voir les résultats</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_voted">
@ -3040,8 +3040,8 @@
<string name="legals_home_server_title">La politique de votre serveur daccueil</string>
<string name="template_legals_application_title">Politique de ${app_name}</string>
<string name="analytics_opt_in_list_item_3">Vous pouvez désactiver ceci à tout moment dans les paramètres</string>
<string name="analytics_opt_in_list_item_2">Nous <b>ne</b> partageons <b>pas</b> dinformation avec des tierces parties</string>
<string name="analytics_opt_in_list_item_1">Nous <b>n</b>enregistrons ou ne profilons <b>aucune</b> donnée du compte</string>
<string name="analytics_opt_in_list_item_2">Nous ne partageons <b>aucune</b> information avec des tiers</string>
<string name="analytics_opt_in_list_item_1">Nous nenregistrons ou ne profilons <b>aucune</b> donnée du compte</string>
<string name="analytics_opt_in_content_link">ici</string>
<string name="analytics_opt_in_content">Aidez nous à identifier les problèmes et améliorer Element en envoyant des rapports dusage anonymes. Pour comprendre de quelle manière les gens utilisent Element sur plusieurs appareils, nous créeront un identifiant aléatoire commun à tous vos appareils.
\n
@ -3061,8 +3061,41 @@
<string name="room_member_override_nick_color">Outrepasser la couleur du pseudo</string>
<string name="login_splash_already_have_account">Jai déjà un compte</string>
<string name="ftue_auth_carousel_4_title">Être vicTeams, ce nest pas Slack vous voulez.</string>
<string name="ftue_auth_carousel_3_title">Se connecter avec qui vous voulez.</string>
<string name="ftue_auth_carousel_3_title">Messagerie sécurisée.</string>
<string name="ftue_auth_carousel_2_title">Vous êtes aux commandes.</string>
<string name="ftue_auth_carousel_1_title">Contrôlez vos conversations.</string>
<string name="room_error_access_unauthorized">Vous nêtes pas autorisé à rejoindre ce salon</string>
<string name="ftue_auth_carousel_body_control">Choisissez où vos conversations sont stockées, vous avez le contrôle et êtes indépendant. Connecté via Matrix.</string>
<string name="room_unsupported_e2e_algorithm">Le chiffrement a été mal configuré ce qui vous empêche denvoyer des messages. Veuillez contacter un administrateur pour remettre le chiffrement en état de marche.</string>
<string name="tooltip_attachment_location">Partager la localisation</string>
<string name="labs_render_locations_in_timeline">Afficher les localisations de l\'utilisateur dans le temps</string>
<string name="settings_enable_location_sharing_summary">Une fois activé il vous sera possible d\'envoyer votre localisation dans n\'importe quel salon</string>
<string name="settings_enable_location_sharing">Activer le partage de localisation</string>
<string name="location_share_external">Ouvrir avec</string>
<string name="template_location_not_available_dialog_content">${app_name} n\'a pas pu accéder à votre localisation. Veuillez réessayer plus tard.</string>
<string name="template_location_not_available_dialog_title">${app_name} n\'a pas pu accéder à votre localisation</string>
<string name="location_share">Partager la localisation</string>
<string name="a11y_location_share_icon">Partager la localisation</string>
<string name="location_activity_title_preview">Localisation</string>
<string name="location_activity_title_static_sharing">Partager la localisation</string>
<string name="closed_poll_option_description">Les résultats ne sont dévoilés que lorsque vous terminez le sondage</string>
<string name="closed_poll_option_title">Sondage fermé</string>
<string name="open_poll_option_description">Les votants voient les résultats dès qu\'ils ont votés</string>
<string name="open_poll_option_title">Ouvrir le sondage</string>
<string name="poll_type_title">Type de sondage</string>
<string name="edit_poll_button">ÉDITER LE SONDAGE</string>
<string name="edit_poll_title">Éditer le sondage</string>
<string name="poll_no_votes_cast">Aucun vote exprimé</string>
<string name="a11y_trust_level_misconfigured">Niveau de confiance mal configuré</string>
<string name="encryption_misconfigured">Le chiffrement est mal configuré</string>
<string name="room_profile_section_restore_security">Restaurer le Chiffrement</string>
<string name="contact_admin_to_restore_encryption">Veuillez contacter un administrateur pour reconfigurer le chiffrement.</string>
<string name="encryption_has_been_misconfigured">Le chiffrement a été mal configuré.</string>
<string name="sent_location">On partagé leur localisation</string>
<string name="login_splash_create_account">Créer un compte</string>
<string name="ftue_auth_carousel_title_messaging">Messagerie pour votre équipe.</string>
<string name="ftue_auth_carousel_body_encrypted">Chiffré de bout en bout et aucun numéro de téléphone n\'est nécessaire. Pas de pubs ni de collecte de données.</string>
<string name="ftue_auth_carousel_body_secure">Communication indépendante et sécurisée qui vous donne le même niveau d\'intimité qu\'une discussion face-à-face dans votre maison.</string>
<string name="attachment_type_location">Localisation</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Le chiffrement a été mal configuré ce qui vous empêche d\'envoyer des messages. Cliquez pour ouvrir les paramètres.</string>
</resources>

View File

@ -777,7 +777,7 @@ Figyelmeztetés: ez a fájl törlésre kerülhet, ha az alkalmazást törli.</st
<string name="start_voice_call_prompt_msg">Biztos, hogy hanghívást akarsz indítani\?</string>
<string name="start_video_call_prompt_msg">Biztos, hogy videóhívást akarsz indítani\?</string>
<string name="groups_list">Csoportok listája</string>
<string name="room_participants_ban_prompt_msg">A felhasználó kitiltása kirúgja őt a szobából, és megakadályozza, hogy újra csatlakozhasson.</string>
<string name="room_participants_ban_prompt_msg">A felhasználó kitiltása eltávolítja őt a szobából, és megakadályozza, hogy újra csatlakozhasson.</string>
<string name="room_settings_all_messages_noisy">Összes üzenet (hangos)</string>
<string name="room_settings_all_messages">Összes üzenet</string>
<string name="room_settings_mention_only">Csak megemlítések</string>
@ -1009,7 +1009,7 @@ Matrixban az üzenetek láthatósága hasonlít az e-mailre. Az üzenet törlés
<string name="settings_show_read_receipts">Olvasás visszajelzés megjelenítése</string>
<string name="settings_show_read_receipts_summary">További információér kattints az olvasás visszaigazolásokra.</string>
<string name="settings_show_join_leave_messages">Be-, és kilépési események megjelenítése</string>
<string name="settings_show_join_leave_messages_summary">Meghívások, kirúgások és kitiltások nem változtak.</string>
<string name="settings_show_join_leave_messages_summary">Meghívások, eltávolítások és kitiltások nem változtak.</string>
<string name="settings_show_avatar_display_name_changes_messages">Fiók események megjelenítése</string>
<string name="settings_show_avatar_display_name_changes_messages_summary">Avatar és név változásokat tartalmaz.</string>
<string name="settings_password">Jelszó</string>
@ -2751,7 +2751,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
<string name="room_settings_room_read_history_dialog_subtitle">A üzenetek olvashatóságának változtatása csak az új üzenetekre lesz érvényes. A régi üzenetek láthatósága nem fog változni.</string>
<string name="settings_show_emoji_keyboard_summary">Az üzenet szerkesztőhöz emodzsi billentyűzet gomb hozzáadása</string>
<string name="settings_show_emoji_keyboard">Emodzsi billentyűzet megjelenítése</string>
<string name="settings_show_room_member_state_events_summary">Magába foglalja a meghívás/belépés/kilépés/kirúgás/kitiltás eseményt és profilkép/megjelenítési név változtatást.</string>
<string name="settings_show_room_member_state_events_summary">Magába foglalja a meghívás/belépés/kilépés/eltávolítás/kitiltás eseményt és profilkép/megjelenítési név változtatást.</string>
<string name="settings_chat_effects_description">Használd a /confetti parancsot vagy küldj üzenetet ami ❄️-t vagy 🎉-t tartalmaz</string>
<string name="settings_chat_effects_title">Beszélgetés effektek megjelenítése</string>
<string name="settings_show_room_member_state_events">Szoba tagok státusz eseményeinek megjelenítése</string>
@ -3015,7 +3015,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
<item quantity="other">Eredmény %1$d szavazat alapján</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
<item quantity="one">Nem szavazott senki</item>
<item quantity="one">%1$d leadott szavazat. Szavazz az eredmények megtekintéséhez</item>
<item quantity="other">%1$d leadott szavazat. Szavazz az eredmények megtekintéséhez</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_voted">
@ -3061,6 +3061,39 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
<string name="room_member_override_nick_color">Becenév színének megváltoztatása</string>
<string name="login_splash_already_have_account">Már van fiókom</string>
<string name="ftue_auth_carousel_4_title">Rázd fel a csoportjaidat.</string>
<string name="ftue_auth_carousel_3_title">Lépj kapcsolatba bárkivel.</string>
<string name="ftue_auth_carousel_3_title">Biztonságos üzenetküldés.</string>
<string name="ftue_auth_carousel_2_title">Te irányítasz.</string>
<string name="tooltip_attachment_location">Tartózkodási hely megosztása</string>
<string name="labs_render_locations_in_timeline">A felhasználó földrajzi helyzetének megjelenítése az idővonalon</string>
<string name="settings_enable_location_sharing_summary">A beállítás után bármelyik szobában megoszthatod a földrajzi helyzetedet</string>
<string name="settings_enable_location_sharing">Földrajzi hely megosztás engedélyezése</string>
<string name="location_share_external">Megnyitás ezzel</string>
<string name="template_location_not_available_dialog_content">${app_name} nem fér hozzá a helyadatodhoz. Próbáld újra később.</string>
<string name="template_location_not_available_dialog_title">${app_name} nem fér hozzá a földrajzi helyzetedhez</string>
<string name="location_share">Tartózkodási hely megosztása</string>
<string name="a11y_location_share_icon">Tartózkodási hely megosztása</string>
<string name="location_activity_title_preview">Földrajzi helyzet</string>
<string name="location_activity_title_static_sharing">Tartózkodási hely megosztása</string>
<string name="closed_poll_option_description">Az eredmény csak a szavazás végeztével válik láthatóvá</string>
<string name="closed_poll_option_title">Lezárt szavazás</string>
<string name="open_poll_option_description">A szavazók a szavazásuk után látják a szavazatokat</string>
<string name="open_poll_option_title">Szavazás megnyitása</string>
<string name="poll_type_title">Szavazás típusa</string>
<string name="edit_poll_button">SZAVAZÁS SZERKESZTÉSE</string>
<string name="edit_poll_title">Szavazás szerkesztése</string>
<string name="poll_no_votes_cast">Nem adtak le szavazatot</string>
<string name="a11y_trust_level_misconfigured">Megbízhatósági szint beállítási hiba</string>
<string name="encryption_misconfigured">A titkosítási beállítás hibás</string>
<string name="room_profile_section_restore_security">Titkosítás helyreállítása</string>
<string name="contact_admin_to_restore_encryption">Kérjük vedd fel a kapcsolatot az adminisztrátorral a titkosítás helyreállítása érdekében.</string>
<string name="encryption_has_been_misconfigured">A titkosítás beállítása hibás.</string>
<string name="sent_location">A földrajzi helyzetüket megosztották</string>
<string name="login_splash_create_account">Fiók létrehozása</string>
<string name="ftue_auth_carousel_title_messaging">Üzenetküldés a csoportodnak.</string>
<string name="ftue_auth_carousel_body_encrypted">Telefonszám nélkül végpontok között titkosított. Reklámok és adatbányászat nélkül.</string>
<string name="ftue_auth_carousel_body_control">Válaszd meg hol legyenek a beszélgetéseid tárolva, visszaadja az irányítást és függetlenné tesz. Csatlakozva a Matrixhoz.</string>
<string name="ftue_auth_carousel_body_secure">Biztonságos és független kommunikáció ami olyan biztonságos mintha valakivel négyszemközt beszélgetnél a házadban.</string>
<string name="attachment_type_location">Földrajzi helyzet</string>
<string name="room_unsupported_e2e_algorithm_as_admin">A titkosítás beállítása hibás így nem lehet üzenetet küldeni. Kattints a beállításokért.</string>
<string name="room_unsupported_e2e_algorithm">A titkosítás beállítása hibás így nem lehet üzenetet küldeni. Kérjük vedd fel a kapcsolatot az adminisztrátorral a titkosítás helyreállításához.</string>
</resources>

View File

@ -3006,7 +3006,40 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
<string name="room_member_override_nick_color">Ubah warna nama tampilan</string>
<string name="login_splash_already_have_account">Saya sudah punya akun</string>
<string name="ftue_auth_carousel_4_title">Bebaskan.</string>
<string name="ftue_auth_carousel_3_title">Hubung dengan siapa pun.</string>
<string name="ftue_auth_carousel_3_title">Perpesanan yang aman.</string>
<string name="ftue_auth_carousel_2_title">Anda dalam kendali.</string>
<string name="ftue_auth_carousel_1_title">Miliki percakapan Anda.</string>
<string name="tooltip_attachment_location">Bagikan lokasi</string>
<string name="labs_render_locations_in_timeline">Tampilkan lokasi pengguna di linimasa</string>
<string name="settings_enable_location_sharing_summary">Setelah diaktifkan Anda akan dapat mengirim lokasi Anda ke ruangan apa saja</string>
<string name="settings_enable_location_sharing">Aktifkan pembagian lokasi</string>
<string name="location_share_external">Buka dengan</string>
<string name="template_location_not_available_dialog_content">${app_name} tidak dapat mengakses lokasi Anda. Silakan coba lagi nanti.</string>
<string name="template_location_not_available_dialog_title">${app_name} tidak dapat mengakses lokasi Anda</string>
<string name="location_share">Bagikan lokasi</string>
<string name="a11y_location_share_icon">Bagikan lokasi</string>
<string name="location_activity_title_preview">Lokasi</string>
<string name="location_activity_title_static_sharing">Bagikan lokasi</string>
<string name="closed_poll_option_description">Hasil akan ditampilkan ketika Anda mengakhiri poll-nya</string>
<string name="closed_poll_option_title">Poll tertutup</string>
<string name="open_poll_option_description">Pemberi suara akan melihat hasilnya ketika telah memberikan suara</string>
<string name="open_poll_option_title">Buka poll</string>
<string name="poll_type_title">Tipe poll</string>
<string name="edit_poll_button">EDIT POLL</string>
<string name="edit_poll_title">Edit poll</string>
<string name="poll_no_votes_cast">Tidak ada suara</string>
<string name="a11y_trust_level_misconfigured">Tingkat kepercayaan dikonfigurasi dengan salah</string>
<string name="encryption_misconfigured">Enkripsi dikonfigurasi dengan salah</string>
<string name="room_profile_section_restore_security">Pulihkan Enkripsi</string>
<string name="contact_admin_to_restore_encryption">Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid.</string>
<string name="encryption_has_been_misconfigured">Enkripsi telah dikonfigurasi dengan salah.</string>
<string name="sent_location">Membagikan lokasinya</string>
<string name="login_splash_create_account">Buat akun</string>
<string name="ftue_auth_carousel_title_messaging">Perpesanan untuk tim Anda.</string>
<string name="ftue_auth_carousel_body_encrypted">Terenkripsi secara ujung-ke-ujung dan tidak memerlukan nomor telepon. Tidak ada iklan atau penambangan data.</string>
<string name="ftue_auth_carousel_body_control">Anda pilih di mana percakapan Anda disimpan, memberikan Anda kendali dan kebebasan. Terhubung via Matrix.</string>
<string name="ftue_auth_carousel_body_secure">Komunikasi aman dan independen yang memberikan tingkat privasi yang sama seperti percakapan wajah-ke-wajah di dalam rumah Anda sendiri.</string>
<string name="attachment_type_location">Lokasi</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Klik untuk membuka pengaturan.</string>
<string name="room_unsupported_e2e_algorithm">Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid.</string>
</resources>

View File

@ -846,7 +846,7 @@
<item quantity="one">%d utente</item>
<item quantity="other">%d utenti</item>
</plurals>
<string name="room_participants_ban_prompt_msg">Bandire l\'utente lo espellerà dalla stanza e gli impedirà di rientrare.</string>
<string name="room_participants_ban_prompt_msg">Bandire l\'utente lo rimuoverà da questa stanza e gli impedirà di rientrare.</string>
<plurals name="room_new_messages_notification">
<item quantity="one">%d nuovo messaggio</item>
<item quantity="other">%d nuovi messaggi</item>
@ -1081,7 +1081,7 @@
<string name="settings_show_read_receipts">Mostra le conferme di lettura</string>
<string name="settings_show_read_receipts_summary">Clicca sulle conferme di lettura per vederne l\'elenco dettagliato.</string>
<string name="settings_show_join_leave_messages">Mostra entrata ed uscita degli utenti</string>
<string name="settings_show_join_leave_messages_summary">Inviti, butta fuori e ban non vengono considerati.</string>
<string name="settings_show_join_leave_messages_summary">Inviti, rimozioni e ban non vengono considerati.</string>
<string name="settings_show_avatar_display_name_changes_messages">Mostra i cambiamenti degli account</string>
<string name="settings_show_avatar_display_name_changes_messages_summary">Includi cambiamenti dell\'avatar e del nome visualizzato.</string>
<string name="settings_password">Password</string>
@ -2333,7 +2333,7 @@
<item quantity="other">%d secondi</item>
</plurals>
<string name="settings_show_room_member_state_events">Mostra eventi di stato dei membri della stanza</string>
<string name="settings_show_room_member_state_events_summary">Includi eventi di invito/entrata/uscita/kick/ban e modifiche di avatar/nome.</string>
<string name="settings_show_room_member_state_events_summary">Includi eventi di invito/entrata/uscita/rimozione/ban e modifiche di avatar/nome.</string>
<string name="sent_a_poll">Sondaggio</string>
<string name="sent_a_reaction">Reagito con: %s</string>
<string name="sent_verification_conclusion">Verifica conclusa</string>
@ -2946,7 +2946,7 @@
<string name="space_permissions_notice">Seleziona i ruoli necessari per cambiare varie parti dello spazio</string>
<string name="space_settings_permissions_subtitle">Vedi e aggiorna i ruoli necessari per cambiare varie parti dello spazio.</string>
<string name="space_participants_unban_prompt_msg">Riammettere l\'utente gli permetterà di rientrare nello spazio.</string>
<string name="space_participants_ban_prompt_msg">Bandire l\'utente lo espellerà da questo spazio e gli impedirà di rientrare.</string>
<string name="space_participants_ban_prompt_msg">Bandire l\'utente lo rimuoverà da questo spazio e gli impedirà di rientrare.</string>
<string name="space_participants_remove_prompt_msg">buttando fuori l\'utente verrà rimosso da questo spazio.
\n
\nPer impedire che possa rientrare, dovresti invece bandirlo.</string>
@ -3006,7 +3006,7 @@
<item quantity="other">Risultato finale basato su %1$d voti</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
<item quantity="one">Nessun voto</item>
<item quantity="one">%1$d voto. Vota per vedere i risultati</item>
<item quantity="other">%1$d voti. Vota per vedere i risultati</item>
</plurals>
<plurals name="poll_total_vote_count_before_ended_and_voted">
@ -3051,7 +3051,40 @@
<string name="room_member_override_nick_color">Sovrascrivi colore nick</string>
<string name="login_splash_already_have_account">Ho già un account</string>
<string name="ftue_auth_carousel_4_title">Riduci il carico ai team.</string>
<string name="ftue_auth_carousel_3_title">Connettiti con chiunque.</string>
<string name="ftue_auth_carousel_3_title">Messaggistica sicura.</string>
<string name="ftue_auth_carousel_2_title">Tu hai il controllo.</string>
<string name="ftue_auth_carousel_1_title">Prendi il controllo delle tue conversazioni.</string>
<string name="tooltip_attachment_location">Condividi posizione</string>
<string name="labs_render_locations_in_timeline">Mostra le posizioni dell\'utente nella linea temporale</string>
<string name="settings_enable_location_sharing_summary">Una volta attivata, potrai inviare la tua posizione in qualsiasi stanza</string>
<string name="settings_enable_location_sharing">Attiva condivisione posizione</string>
<string name="location_share_external">Apri con</string>
<string name="template_location_not_available_dialog_content">${app_name} non ha potuto rilevare la tua posizione. Riprova più tardi.</string>
<string name="template_location_not_available_dialog_title">${app_name} non ha potuto rilevare la tua posizione</string>
<string name="location_share">Condividi posizione</string>
<string name="a11y_location_share_icon">Condividi posizione</string>
<string name="location_activity_title_preview">Posizione</string>
<string name="location_activity_title_static_sharing">Condividi posizione</string>
<string name="closed_poll_option_description">I risultati verranno rivelati solo quando termini il sondaggio</string>
<string name="closed_poll_option_title">Sondaggio chiuso</string>
<string name="open_poll_option_description">I votanti vedono i risultati appena avranno votato</string>
<string name="open_poll_option_title">Apri sondaggio</string>
<string name="poll_type_title">Tipo sondaggio</string>
<string name="edit_poll_button">MODIFICA SONDAGGIO</string>
<string name="edit_poll_title">Modifica sondaggio</string>
<string name="poll_no_votes_cast">Nessun voto</string>
<string name="a11y_trust_level_misconfigured">Livello di fiducia configurato male</string>
<string name="encryption_misconfigured">La crittografia è configurata male</string>
<string name="room_profile_section_restore_security">Reimposta crittografia</string>
<string name="contact_admin_to_restore_encryption">Contatta un amministratore per reimpostare la crittografia ad uno stato valido.</string>
<string name="encryption_has_been_misconfigured">La crittografia è stata configurata male.</string>
<string name="sent_location">Ha condiviso la sua posizione</string>
<string name="login_splash_create_account">Crea account</string>
<string name="ftue_auth_carousel_title_messaging">Messaggistica per la tua squadra.</string>
<string name="ftue_auth_carousel_body_encrypted">Crittografia end-to-end e nessun numero di telefono richiesto. Niente pubblicità o raccolta di dati.</string>
<string name="ftue_auth_carousel_body_control">Scegli dove vengono tenute le tue conversazioni, dandoti controllo e indipendenza. Connesso via Matrix.</string>
<string name="ftue_auth_carousel_body_secure">Comunicazioni sicure e indipendenti che ti danno lo stesso livello di riservatezza di una conversazione faccia a faccia in casa tua.</string>
<string name="attachment_type_location">Posizione</string>
<string name="room_unsupported_e2e_algorithm_as_admin">La crittografia è stata configurata male, perciò non puoi inviare messaggi. Clicca per aprire le impostazioni.</string>
<string name="room_unsupported_e2e_algorithm">La crittografia è stata configurata male, perciò non puoi inviare messaggi. Contatta l\'amministratore per reimpostare la crittografia ad uno stato valido.</string>
</resources>

View File

@ -819,7 +819,7 @@
<string name="option_take_video">Tirar vídeo</string>
<string name="groups_list">Lista de Grupos</string>
<string name="call">Chamar</string>
<string name="room_participants_ban_prompt_msg">Banir usuária(o) vai expulsá-la(o) desta sala e preveni-la(o) de se juntar de novo.</string>
<string name="room_participants_ban_prompt_msg">Banir usuária(o) vai removê-la(o) desta sala e preveni-la(o) de se juntar de novo.</string>
<string name="room_settings_all_messages_noisy">Todas as mensagens (barulhento)</string>
<string name="room_settings_all_messages">Todas as mensagens</string>
<string name="room_settings_mention_only">Menções somente</string>
@ -1118,7 +1118,7 @@
<string name="settings_show_read_receipts">Mostrar recibos de leitura</string>
<string name="settings_show_read_receipts_summary">Clique nos recibos de leitura para uma lista detalhada.</string>
<string name="settings_show_join_leave_messages">Mostrar eventos de juntar-se e sair</string>
<string name="settings_show_join_leave_messages_summary">Convites, expulsões e bans são desafetados.</string>
<string name="settings_show_join_leave_messages_summary">Convites, remoções e bans são desafetados.</string>
<string name="settings_show_avatar_display_name_changes_messages">Mostrar eventos de conta</string>
<string name="settings_show_avatar_display_name_changes_messages_summary">Inclui mudanças de avatar e nome de exibição.</string>
<string name="startup_notification_fdroid_battery_optim_title">Conexão no Background</string>
@ -2334,7 +2334,7 @@
<item quantity="one">%d segundo</item>
<item quantity="other">%d segundos</item>
</plurals>
<string name="settings_show_room_member_state_events_summary">Inclui eventos de convite/juntar-se/saiu/expulsão/ban e mudanças de avatar/nome de exibição.</string>
<string name="settings_show_room_member_state_events_summary">Inclui eventos de convidar/juntar-se/saiu/remover/ban e mudanças de avatar/nome de exibição.</string>
<string name="settings_emails_and_phone_numbers_title">Emails e números de telefone</string>
<string name="settings_emails_and_phone_numbers_summary">Gerenciar emails e números de telefone linkados a sua conta Matrix</string>
<string name="settings_text_message_sent_hint">Código</string>
@ -2956,7 +2956,7 @@
<string name="space_settings_permissions_subtitle">Veja e atualize os papéis requeridos para mudar várias partes do espaço.</string>
<string name="space_settings_permissions_title">Permissões de espaço</string>
<string name="space_participants_unban_prompt_msg">Desbanir usuária(o) vai permiti-la(o) se juntar ao espaço de novo.</string>
<string name="space_participants_ban_prompt_msg">Banir usuária(o) vai expulsá-la(o) deste espaço e preveni-la de se juntar de novo.</string>
<string name="space_participants_ban_prompt_msg">Banir usuária(o) vai removê-la(o) deste espaço e preveni-la de se juntar de novo.</string>
<string name="space_participants_remove_prompt_msg">expulsar usuária(o) vai removê-la(o) deste espaço.
\n
\nPara preveni-la(o) de se juntar de novo, você devia bani-la(o) em vez disso.</string>
@ -3061,7 +3061,40 @@
<string name="room_member_override_nick_color">Sobrepor cor de nick</string>
<string name="login_splash_already_have_account">Eu já tenho uma conta</string>
<string name="ftue_auth_carousel_4_title">Dê liberdade a times.</string>
<string name="ftue_auth_carousel_3_title">Conecte-se com qualquer pessoa.</string>
<string name="ftue_auth_carousel_3_title">Mensageria segura.</string>
<string name="ftue_auth_carousel_2_title">Você está em controle.</string>
<string name="ftue_auth_carousel_1_title">Tenha posse de suas conversas.</string>
<string name="tooltip_attachment_location">Compartilhar localização</string>
<string name="labs_render_locations_in_timeline">Render localizações de usuária(o) na timeline</string>
<string name="settings_enable_location_sharing_summary">Uma vez habilitada você vai ser capaz de enviar sua localização a qualquer sala</string>
<string name="settings_enable_location_sharing">Habilitar compartilhamento de localização</string>
<string name="location_share_external">Abrir com</string>
<string name="template_location_not_available_dialog_content">${app_name} não pôde acessar sua localização. Por favor tente de novo mais tarde.</string>
<string name="template_location_not_available_dialog_title">${app_name} não pôde acessar sua localização</string>
<string name="location_share">Compartilhar localização</string>
<string name="a11y_location_share_icon">Compartilhar localização</string>
<string name="location_activity_title_preview">Localização</string>
<string name="location_activity_title_static_sharing">Compartilhar localização</string>
<string name="closed_poll_option_description">Resultados são somente revelados quando você termina a sondagem</string>
<string name="closed_poll_option_title">Sondagem fechada</string>
<string name="open_poll_option_description">Votantes veem resultados assim que elas(es) têm votado</string>
<string name="open_poll_option_title">Sondagem aberta</string>
<string name="poll_type_title">Tipo de sondagem</string>
<string name="edit_poll_button">EDITAR SONDAGEM</string>
<string name="edit_poll_title">Editar sondagem</string>
<string name="poll_no_votes_cast">Nenhum voto lançado</string>
<string name="a11y_trust_level_misconfigured">Nível de confiança malconfigurado</string>
<string name="encryption_misconfigured">Encriptação está malconfigurada</string>
<string name="room_profile_section_restore_security">Restaurar Encriptação</string>
<string name="contact_admin_to_restore_encryption">Por favor contacte um(a) admin para restaurar encriptação a um estado válido.</string>
<string name="sent_location">Compartilhou a localização dela(e)</string>
<string name="attachment_type_location">Localização</string>
<string name="encryption_has_been_misconfigured">Encriptação tem sido malconfigurada.</string>
<string name="login_splash_create_account">Criar conta</string>
<string name="ftue_auth_carousel_title_messaging">Mensageria para seu time.</string>
<string name="ftue_auth_carousel_body_encrypted">Encriptado ponta-a-ponta e nenhum número de telefone requerido. Sem publicidade ou datamining.</string>
<string name="ftue_auth_carousel_body_control">Escolha onde suas conversas são mantidas, dando-lhe controle e independência. Conectado via Matrix.</string>
<string name="ftue_auth_carousel_body_secure">Comunicação segura e independente que lhe dá o mesmo nível de privacidade que conversa face-a-face em sua própria casa.</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Encriptação tem sido malconfigurada então você não pode enviar mensagens. Clique para abrir configurações.</string>
<string name="room_unsupported_e2e_algorithm">Encriptação tem sido malconfigurada então você não pode enviar mensagens. Por favor contacte um(a) admin para restaurar encriptação a um estado válido.</string>
</resources>

View File

@ -795,7 +795,7 @@
<string name="start_voice_call_prompt_msg">Ste si istý, že chcete začať hlasový hovor?</string>
<string name="start_video_call_prompt_msg">Ste si istí, že chcete začať video hovor?</string>
<string name="groups_list">Zoznam komunít</string>
<string name="room_participants_ban_prompt_msg">Zakázanie používateľa ho vylúči z tejto miestnosti a zabráni mu v ďalšom vstupe.</string>
<string name="room_participants_ban_prompt_msg">Zakázanie používateľa ho odstráni z tejto miestnosti a zabráni mu v ďalšom vstupe.</string>
<string name="room_settings_all_messages_noisy">Všetky správy (hlasné)</string>
<string name="room_settings_all_messages">Všetky správy</string>
<string name="room_settings_mention_only">Len zmienky</string>
@ -2176,4 +2176,106 @@
<string name="notice_room_server_acl_set_ip_literals_allowed">• Servery s číslicami ako IP adresy sú povolené.</string>
<string name="notice_room_server_acl_set_allowed">• Servery, ktorých názov sa zhoduje s %s sú povolené.</string>
<string name="notice_room_server_acl_set_banned">• Servery, ktorých názov sa zhoduje s %s sú zakázané.</string>
<string name="power_level_edit_title">Nastaviť rolu</string>
<string name="recovery_passphrase">Fráza pre obnovenie</string>
<string name="settings_category_composer">Editor správ</string>
<string name="verification_scan_emoji_title">Nedá sa skenovať</string>
<string name="devices_other_devices">Iné relácie</string>
<string name="devices_current_device">Aktuálna relácia</string>
<string name="soft_logout_clear_data_dialog_title">Vyčistiť údaje</string>
<string name="soft_logout_clear_data_dialog_submit">Vyčistiť údaje</string>
<string name="legals_third_party_notices">Knižnice tretích strán</string>
<string name="preference_help_title">Pomoc a podpora</string>
<string name="invite_by_username_or_mail">Pozvať pomocou používateľského mena alebo e-mailu</string>
<string name="labs_enable_latex_maths">Povoliť matematiku LaTeX</string>
<string name="restart_the_application_to_apply_changes">Reštartujte aplikáciu, aby sa zmena prejavila.</string>
<string name="ftue_auth_carousel_2_title">Vy máte všetko pod kontrolou.</string>
<string name="ftue_auth_carousel_3_title">Bezpečné zasielanie správ.</string>
<string name="labs_auto_report_uisi">Automatické hlásenie chýb dešifrovania.</string>
<string name="labs_auto_report_uisi_desc">Váš systém bude automaticky odosielať záznamy, keď sa vyskytne chyba dešifrovania</string>
<string name="tooltip_attachment_photo">Otvoriť kameru</string>
<string name="tooltip_attachment_gallery">Posielajte obrázky a videá</string>
<string name="tooltip_attachment_sticker">Odoslať nálepku</string>
<string name="tooltip_attachment_contact">Otvoriť kontakty</string>
<string name="tooltip_attachment_poll">Vytvoriť anketu</string>
<string name="ftue_auth_carousel_body_secure">Bezpečná a nezávislá komunikácia, ktorá vám poskytuje rovnakú úroveň súkromia ako rozhovor zoči-voči vo vašom vlastnom dome.</string>
<string name="ftue_auth_carousel_body_control">Vyberte si, kde budú vaše rozhovory uložené, a získajte tak kontrolu a nezávislosť. Pripojené cez Matrix.</string>
<string name="ftue_auth_carousel_body_encrypted">End-to-end šifrovanie a bez potreby telefónneho čísla. Žiadne reklamy ani zneužívanie údajov.</string>
<string name="ftue_auth_carousel_title_messaging">Zasielanie správ pre váš tím.</string>
<string name="login_splash_create_account">Vytvoriť účet</string>
<string name="encryption_has_been_misconfigured">Šifrovanie je nesprávne nastavené.</string>
<string name="contact_admin_to_restore_encryption">Obráťte sa na správcu, aby obnovil platný stav šifrovania.</string>
<string name="room_profile_section_restore_security">Obnoviť šifrovanie</string>
<string name="encryption_misconfigured">Šifrovanie je nesprávne nastavené</string>
<string name="poll_no_votes_cast">Žiadne odovzdané hlasy</string>
<string name="edit_poll_title">Upraviť anketu</string>
<string name="closed_poll_option_description">Výsledky sa zobrazia až po ukončení ankety</string>
<string name="attachment_type_location">Poloha</string>
<string name="sent_location">Zdieľali svoju polohu</string>
<string name="location_activity_title_static_sharing">Zdieľať polohu</string>
<string name="location_activity_title_preview">Poloha</string>
<string name="a11y_location_share_icon">Zdieľať polohu</string>
<string name="location_share">Zdieľať polohu</string>
<string name="template_location_not_available_dialog_title">${app_name} nemohol získať prístup k vašej polohe</string>
<string name="template_location_not_available_dialog_content">${app_name} nemohol získať prístup k vašej polohe. Skúste to prosím neskôr.</string>
<string name="location_share_external">Otvoriť s</string>
<string name="settings_enable_location_sharing">Povoliť zdieľanie polohy</string>
<string name="settings_enable_location_sharing_summary">Po zapnutí tejto funkcie budete môcť odoslať svoju polohu do ľubovoľnej miestnosti</string>
<string name="labs_render_locations_in_timeline">Vykresliť umiestnenie používateľa na časovej osi</string>
<string name="tooltip_attachment_location">Zdieľať polohu</string>
<string name="login_server_url_form_modular_text">Prémiový hosting pre organizácie</string>
<string name="login_signin_to">Prihlásiť sa do %1$s</string>
<string name="login_connect_to_a_custom_server">Pripojiť sa k vlastnému serveru</string>
<string name="login_connect_to_modular">Pripojiť sa k službám Element Matrix</string>
<string name="login_connect_to">Pripojiť sa k %1$s</string>
<string name="login_server_modular_text">Prémiový hosting pre organizácie</string>
<string name="login_server_matrix_org_text">Pripojte sa k miliónom ľudí zadarmo na najväčšom verejnom serveri</string>
<string name="login_server_text">Rovnako ako e-mail, aj účty majú jeden domov, hoci môžete hovoriť s kýmkoľvek</string>
<string name="login_splash_submit">Začnite</string>
<string name="login_splash_text3">Rozšírte a prispôsobte si možnosti</string>
<string name="login_splash_text2">Udržujte konverzácie súkromné pomocou šifrovania</string>
<string name="login_splash_text1">Komunikujte s ľuďmi priamo alebo v skupinách</string>
<string name="login_splash_title">Je to vaša konverzácia. Vlastnite ju.</string>
<string name="room_join_rules_invite_by_you">Vytvorili ste miestnosť len pre pozvaných.</string>
<string name="room_join_rules_invite">%1$s vytvoril miestnosť len na pozvanie.</string>
<string name="room_join_rules_public_by_you">Miestnosť ste zverejnili pre každého, kto pozná odkaz.</string>
<string name="room_join_rules_public">%1$s zverejnil miestnosť pre každého, kto pozná odkaz.</string>
<string name="help_long_click_on_room_for_more_options">Dlhým kliknutím na miestnosť zobrazíte ďalšie možnosti</string>
<string name="notice_member_no_changes_by_you">Nevykonali ste žiadne zmeny</string>
<string name="notice_member_no_changes">%1$s nevykonal žiadne zmeny</string>
<string name="content_reported_as_spam_content">Tento obsah bol nahlásený ako spam.
\n
\nAk nechcete vidieť ďalší obsah od tohto používateľa, môžete ho ignorovať a skryť jeho správy.</string>
<string name="content_reported_as_spam_title">Nahlásené ako spam</string>
<string name="content_reported_content">Tento obsah bol nahlásený.
\n
\nAk nechcete vidieť ďalší obsah od tohto používateľa, môžete ho ignorovať a skryť jeho správy.</string>
<string name="report_content_custom_submit">NAHLÁSIŤ</string>
<string name="report_content_custom_hint">Dôvod nahlásenia tohto obsahu</string>
<string name="uploads_files_no_result">V tejto miestnosti nie sú žiadne súbory</string>
<string name="uploads_media_title">MÉDIÁ</string>
<string name="uploads_media_no_result">V tejto miestnosti nie sú žiadne médiá</string>
<string name="error_attachment">Pri načítavaní prílohy došlo k chybe.</string>
<string name="error_file_too_big">Súbor \'%1$s\' (%2$s) je príliš veľký na odoslanie. Limit je %3$s.</string>
<string name="a11y_jump_to_bottom">Prejsť na spodok</string>
<string name="a11y_create_room">Vytvoriť novú miestnosť</string>
<string name="a11y_create_direct_message">Vytvoriť novú priamu konverzáciu</string>
<string name="error_terms_not_accepted">Po prijatí podmienok domovského servera to skúste znova.</string>
<string name="space_settings_permissions_subtitle">Zobraziť a aktualizovať role potrebné na zmenu rôznych častí priestoru.</string>
<string name="room_settings_permissions_subtitle">Zobraziť a aktualizovať role potrebné na zmenu rôznych častí miestnosti.</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Šifrovanie bolo nesprávne nastavené, takže nemôžete odosielať správy. Kliknutím otvorte nastavenia.</string>
<string name="room_unsupported_e2e_algorithm">Šifrovanie bolo nesprávne nastavené, takže nemôžete odosielať správy. Obráťte sa na správcu, aby obnovil platný stav šifrovania.</string>
<string name="space_participants_unban_prompt_msg">Zrušenie zákazu používateľovi mu umožní znovu sa pripojiť do priestoru.</string>
<string name="space_participants_ban_prompt_msg">Zakázanie používateľa ho odstráni z tohto priestoru a zabráni mu v ďalšom vstupe.</string>
<string name="space_participants_remove_prompt_msg">Používateľ bude z tohto priestoru odstránený.
\n
\nAby ste mu zabránili v opätovnom pripojení, mali by ste ho zakázať.</string>
<string name="room_participants_leave_private_warning">Táto miestnosť nie je verejná. Bez pozvánky sa do nej nebudete môcť opätovne pripojiť.</string>
<string name="continue_anyway">Pokračovať aj tak</string>
<string name="permissions_denied_add_contact">Povoľte prístup ku kontaktom.</string>
<string name="permissions_denied_qr_code">Ak chcete skenovať QR kód, musíte povoliť prístup k fotoaparátu.</string>
<string name="call_ended_user_busy_description">Volaný používateľ je obsadený.</string>
<string name="call_ended_user_busy_title">Používateľ je obsadený</string>
<string name="call_held_by_user">%s podržal hovor</string>
<string name="audio_call_with_participant">Hlasový hovor s %s</string>
</resources>

View File

@ -500,7 +500,7 @@
<string name="room_participants_invite_search_another_user">ID Përdoruesi, Emër ose email-i</string>
<string name="room_participants_action_mention">Përmendje</string>
<string name="room_participants_action_devices_list">Shfaq Listë Sesionesh</string>
<string name="room_participants_ban_prompt_msg">Dëbimi i përdoruesit do ta përzërë atë nga kjo dhomë dhe do ti pengojë atij rihyrjen.</string>
<string name="room_participants_ban_prompt_msg">Dëbimi i përdoruesit do ta heqë atë nga kjo dhomë dhe do ti pengojë atij rihyrjen.</string>
<string name="reason_hint">Arsye</string>
<string name="room_participants_invite_prompt_msg">Jeni i sigurt se doni të ftohet %s te kjo fjalosje?</string>
<string name="room_participants_invite_join_names">"%1$s, "</string>
@ -1048,7 +1048,7 @@
<string name="settings_inline_url_preview_summary">Parashihini lidhjet brenda fjalosjes, kur shërbyesi juaj home e mbulon këtë veçori.</string>
<string name="settings_send_typing_notifs_summary">Lejojuni përdoruesve të tjerë të dinë se po shtypni.</string>
<string name="settings_send_markdown_summary">Formatojini mesazhet duke përdorur sintaksën Markdown përpara se të dërgohen. Kjo lejon formatim të thelluar, f.v., përdorimi i yllthit për ta shfaqur tekstin me të pjerrëta.</string>
<string name="settings_show_join_leave_messages_summary">Nuk prek ftesat, përzëniet dhe dëbimet.</string>
<string name="settings_show_join_leave_messages_summary">Nuk prek ftesat, heqjet dhe dëbimet.</string>
<string name="template_startup_notification_privacy_message">${app_name}-i mund të xhirojë në prapaskenë që të administrojë njoftimet tuaja në rrugë të sigurt dhe privatisht. Kjo mund të ndikojë në harxhimin e baterisë.</string>
<string name="template_settings_opt_in_of_analytics_summary">${app_name}-i grumbullon të dhëna analitike anonime që të na lejojë ta përmirësojmë aplikacionin.</string>
<string name="template_settings_opt_in_of_analytics_prompt">Ju lutemi, aktivizoni analizat që të na ndihmoni të përmirësojmë ${app_name}-in.</string>
@ -2201,7 +2201,7 @@
<item quantity="other">%d sekonda</item>
</plurals>
<string name="settings_show_room_member_state_events">Shfaq akte gjendjeje përdoruesish të dhomës</string>
<string name="settings_show_room_member_state_events_summary">Përfshin akte ftimi/pjesëmarrjeje/ikjeje/përzënieje/dëbimi dhe ndryshime emri avatari/shfaqjeje.</string>
<string name="settings_show_room_member_state_events_summary">Përfshin akte ftimi/pjesëmarrjeje/ikjeje/heqjeje/dëbimi dhe ndryshime emri avatari/shfaqjeje.</string>
<string name="settings_secure_backup_section_title">Kopjeruajtje e Sigurt</string>
<string name="settings_secure_backup_manage">Administroni</string>
<string name="settings_secure_backup_setup">Ujdisni Kopjeruajtje të Sigurt</string>
@ -2867,8 +2867,8 @@
<string name="call_tap_to_return">%1$s Prekeni që të kthehet</string>
<string name="call_one_active">Thirrje aktive (%1$s) ·</string>
<plurals name="call_active_status">
<item quantity="one">Thirrje aktive</item>
<item quantity="other">%1$d thirrje aktive</item>
<item quantity="one">Thirrje aktive ·</item>
<item quantity="other">%1$d thirrje aktive ·</item>
</plurals>
<string name="call_tile_connection_failed">Lidhja dështoi</string>
<string name="call_tile_no_answer">Ska përgjigje</string>
@ -2913,7 +2913,7 @@
<string name="you_will_leave_all_in">Do të braktisni krejt dhomat dhe hapësirat te %s.</string>
<string name="leave_all_rooms_and_spaces">Braktis krejt dhomat dhe hapësirat</string>
<string name="space_leave_prompt_msg_with_name">Jeni i sigurt se doni të dilni nga \'%s\?</string>
<string name="discovery_section">Zbulim (%S)</string>
<string name="discovery_section">Zbulim (%s)</string>
<string name="finish_setup">Përfundoje ujdisjen</string>
<string name="discovery_invite">Ftoni me email, gjeni kontakte, etj…</string>
<string name="finish_setting_up_discovery">Përfundo ujdisjen e zbulimit.</string>
@ -2945,7 +2945,7 @@
<string name="space_settings_permissions_subtitle">Të shohë dhe përditësojë role të domosdoshëm për të ndryshuar anë të ndryshme të hapësirës.</string>
<string name="space_settings_permissions_title">Leje hapësire</string>
<string name="space_participants_unban_prompt_msg">Heqja e dëbimit përdoruesit do ti lejojë të marrë pjesë sërish në hapësirë.</string>
<string name="space_participants_ban_prompt_msg">Dëbimi i përdoruesit do ta përzërë atë nga kjo hapësirë dhe do ti pengojë atij rihyrjen.</string>
<string name="space_participants_ban_prompt_msg">Dëbimi i përdoruesit do ta heqë atë nga kjo hapësirë dhe do ti pengojë atij rihyrjen.</string>
<string name="space_participants_remove_prompt_msg">Përzënia e përdoruesit do ta heqë prej kësaj hapësire.
\n
\nQë të pengohet pjesëmarrja sërish e tij, duhet ta dëboni.</string>
@ -3032,8 +3032,8 @@
<string name="analytics_opt_in_title">Ndihmoni të përmirësohet Element-in</string>
<string name="action_enable">Aktivizoje</string>
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
<item quantity="one">Ska vota të hedhura</item>
<item quantity="other">%1$d votë e hedhur. Votoni, që të shihni përfundimet</item>
<item quantity="one">%1$d votë e hedhur. Votoni, që të shihni përfundimet</item>
<item quantity="other">%1$d vota të hedhur. Votoni, që të shihni përfundimet</item>
</plurals>
<string name="restart_the_application_to_apply_changes">Që ndryshimi të hyjë në fuqi, rinisni aplikacionin.</string>
<string name="labs_enable_latex_maths">Aktivizo elementë LaTeX për matematikë</string>
@ -3048,7 +3048,40 @@
<string name="labs_auto_report_uisi">Raporto Vetvetiu Gabime Shfshehtëzimi.</string>
<string name="room_member_override_nick_color">Anashkalo ngjyrë nofke</string>
<string name="login_splash_already_have_account">Kam tashmë një llogari</string>
<string name="ftue_auth_carousel_3_title">Lidhuni me këdo.</string>
<string name="ftue_auth_carousel_3_title">Shkëmbim i siguruar mesazhesh.</string>
<string name="ftue_auth_carousel_2_title">Kontrollin e keni ju.</string>
<string name="ftue_auth_carousel_1_title">Jini zot i bisedave tuaja.</string>
<string name="tooltip_attachment_location">Jepe vendndodhjen</string>
<string name="settings_enable_location_sharing">Aktivizoni dhënie vendndodhjeje</string>
<string name="location_share">Jepe vendndodhjen</string>
<string name="a11y_location_share_icon">Jepe vendndodhjen</string>
<string name="location_activity_title_preview">Vendndodhje</string>
<string name="location_activity_title_static_sharing">Jepe vendndodhjen</string>
<string name="closed_poll_option_description">Përfundimet shfaqen vetëm kur përfundoni anketimin</string>
<string name="closed_poll_option_title">Anketim i mbyllur</string>
<string name="open_poll_option_description">Votuesit do të shohin përfundime sapo të kenë votuar</string>
<string name="open_poll_option_title">Anketim i hapur</string>
<string name="poll_type_title">Lloj anketimi</string>
<string name="login_splash_create_account">Krijoni llogari</string>
<string name="ftue_auth_carousel_title_messaging">Shkëmbim mesazhesh për ekipin tuaj.</string>
<string name="ftue_auth_carousel_body_encrypted">I fshehtëzuar skaj më skaj dhe pa u dashur numër telefoni. Pa reklama, apo shfrytëzim të dhënash.</string>
<string name="ftue_auth_carousel_body_control">Zgjidhni ku mbahen bisedat tuaja, duke ju dhënë kontroll dhe pavarësi. Të lidhur përmes Matrix-i.</string>
<string name="ftue_auth_carousel_body_secure">Komunikim i sigurt dhe i pavarur, që ju jep të njëjtën shkallë privatësie si biseda kokë më kokë, në shtëpinë tuaj.</string>
<string name="labs_render_locations_in_timeline">Trego vendndodhje përdoruesi në rrjedhën kohore</string>
<string name="settings_enable_location_sharing_summary">Pasi të aktivizohet, do të jeni në gjendje të dërgoni vendndodhjen tuaj në çfarëdo dhome</string>
<string name="location_share_external">Hape me</string>
<string name="template_location_not_available_dialog_content">${app_name} snjohu dot vendndodhjen tuaj. Ju lutemi, riprovoni më vonë.</string>
<string name="template_location_not_available_dialog_title">${app_name} smësoi dot vendndodhjen tuaj</string>
<string name="edit_poll_button">PËRPUNONI ANKETIMIN</string>
<string name="edit_poll_title">Përpunoni anketimin</string>
<string name="poll_no_votes_cast">Sjanë hedhur vota</string>
<string name="a11y_trust_level_misconfigured">Shkallë besueshmërie e formësuar gabim</string>
<string name="encryption_misconfigured">Fshehtëzimi është formësuar gabim</string>
<string name="room_profile_section_restore_security">Riktheni Fshehtëzimin</string>
<string name="contact_admin_to_restore_encryption">Ju lutemi, lidhuni me një nga përgjegjësit, që ta ktheni fshehtëzimin në një gjendje të vlefshme.</string>
<string name="encryption_has_been_misconfigured">Fshehtëzimi është formësuar gabim.</string>
<string name="sent_location">Tregoi vendndodhjen e vet</string>
<string name="attachment_type_location">Vendndodhje</string>
<string name="room_unsupported_e2e_algorithm_as_admin">Fshehtëzimi është formësuar keq, që të mos dërgoni mesazhe. Klikoni që të hapni rregullimet.</string>
<string name="room_unsupported_e2e_algorithm">Fshehtëzimi është formësuar keq, që të mos dërgoni mesazhe. Ju lutemi, lidhuni me një nga përgjegjësit që ta ktheni fshehtëzimin në një gjendje të vlefshme.</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More