From 84e23e19119697cb831043212dac5cfc39bb5a46 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 17 Jan 2022 12:13:30 +0000 Subject: [PATCH 01/77] storing the user usecase selection and clearinng on signout --- .../im/vector/app/features/MainActivity.kt | 3 + .../app/features/onboarding/FtueUseCase.kt | 14 +++-- .../onboarding/OnboardingViewModel.kt | 16 +++-- .../onboarding/store/OnboardingStore.kt | 59 +++++++++++++++++++ 4 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index b4706780b7..89ce4a0545 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -36,6 +36,7 @@ import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.notifications.NotificationDrawerManager +import im.vector.app.features.onboarding.store.OnboardingStore import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.UnlockedActivity @@ -98,6 +99,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity @Inject lateinit var pinLocker: PinLocker @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var vectorAnalytics: VectorAnalytics + @Inject lateinit var onboardingStore: OnboardingStore override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -193,6 +195,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() + onboardingStore.clear() } withContext(Dispatchers.IO) { // On BG thread diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index e720b7307c..952612b43f 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -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 } + } } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 43f37f4601..d806a86600 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -42,6 +42,7 @@ import im.vector.app.features.login.LoginMode import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ServerType import im.vector.app.features.login.SignMode +import im.vector.app.features.onboarding.store.OnboardingStore import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain @@ -73,7 +74,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 onboardingStore: OnboardingStore ) : VectorViewModel(initialState) { @AssistedFactory @@ -125,7 +127,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 +460,17 @@ class OnboardingViewModel @AssistedInject constructor( } } - private fun handleUpdateUseCase() { - // TODO act on the use case selection + private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { + viewModelScope.launch { + onboardingStore.setUseCase(action.useCase) + } _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } private fun resetUseCase() { - // TODO remove stored use case + viewModelScope.launch { + onboardingStore.resetUseCase() + } } private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt new file mode 100644 index 0000000000..cc4d324aa0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt @@ -0,0 +1,59 @@ +/* + * 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.onboarding.store + +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 javax.inject.Inject + +private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_onboarding") + +/** + * Local storage for: + * - messaging use case (Enum/String) + */ +class OnboardingStore @Inject constructor( + private val context: Context +) { + 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 { it.clear() } + } +} From 515c8ce7c2c4ea0cce947be1a0fcbe6a70996815 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 20 Jan 2022 10:43:04 +0000 Subject: [PATCH 02/77] adding ability to update the user properties (identity) via the vector analytics --- .../im/vector/app/features/analytics/VectorAnalytics.kt | 6 ++++++ .../features/analytics/impl/DefaultVectorAnalytics.kt | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt index 95322412bd..ddab0412dd 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt @@ -16,6 +16,7 @@ package im.vector.app.features.analytics +import im.vector.app.features.analytics.plan.Identity import kotlinx.coroutines.flow.Flow interface VectorAnalytics : AnalyticsTracker { @@ -58,4 +59,9 @@ interface VectorAnalytics : AnalyticsTracker { * To be called when application is started */ fun init() + + /** + * Update user specific properties + */ + fun updateUserProperties(identity: Identity) } diff --git a/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt index eaf2e42705..d32cef604b 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt @@ -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?.toPostHogProperties(): Properties? { if (this == null) return null From 6c1cbccff36314adbb2bfa9b6e23b56748a44c7c Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 20 Jan 2022 10:43:24 +0000 Subject: [PATCH 03/77] updating the identity with the use case selection as part of the onboarding flow --- .../im/vector/app/features/onboarding/FtueUseCase.kt | 11 +++++++++++ .../app/features/onboarding/OnboardingViewModel.kt | 11 ++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index 952612b43f..23bea3eb99 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -16,6 +16,8 @@ package im.vector.app.features.onboarding +import im.vector.app.features.analytics.plan.Identity + enum class FtueUseCase(val persistableValue: String) { FRIENDS_FAMILY("friends_family"), TEAMS("teams"), @@ -26,3 +28,12 @@ enum class FtueUseCase(val persistableValue: String) { fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue } } } + +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 + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index d806a86600..7ab7662620 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -36,6 +36,8 @@ 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.VectorAnalytics +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 @@ -75,7 +77,8 @@ class OnboardingViewModel @AssistedInject constructor( private val stringProvider: StringProvider, private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, - private val onboardingStore: OnboardingStore + private val onboardingStore: OnboardingStore, + private val vectorAnalytics: VectorAnalytics ) : VectorViewModel(initialState) { @AssistedFactory @@ -463,6 +466,9 @@ class OnboardingViewModel @AssistedInject constructor( private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { viewModelScope.launch { onboardingStore.setUseCase(action.useCase) + vectorAnalytics.updateUserProperties( + Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()) + ) } _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } @@ -470,6 +476,9 @@ class OnboardingViewModel @AssistedInject constructor( private fun resetUseCase() { viewModelScope.launch { onboardingStore.resetUseCase() + vectorAnalytics.updateUserProperties( + Identity(ftueUseCaseSelection = null) + ) } } From 0a7c421faa3f45041d1fae6147ddb5b373bc1506 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 12:55:21 +0000 Subject: [PATCH 04/77] making use of the analytics tracker instead of the vector analytics for the smaller tracking subset --- .../im/vector/app/features/analytics/AnalyticsTracker.kt | 6 ++++++ .../im/vector/app/features/analytics/VectorAnalytics.kt | 6 ------ .../vector/app/features/onboarding/OnboardingViewModel.kt | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt b/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt index e1da0f4434..e85919a45f 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt @@ -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) } diff --git a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt index ddab0412dd..95322412bd 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt @@ -16,7 +16,6 @@ package im.vector.app.features.analytics -import im.vector.app.features.analytics.plan.Identity import kotlinx.coroutines.flow.Flow interface VectorAnalytics : AnalyticsTracker { @@ -59,9 +58,4 @@ interface VectorAnalytics : AnalyticsTracker { * To be called when application is started */ fun init() - - /** - * Update user specific properties - */ - fun updateUserProperties(identity: Identity) } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 7ab7662620..3e4ea68317 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -36,7 +36,7 @@ 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.VectorAnalytics +import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.login.HomeServerConnectionConfigFactory import im.vector.app.features.login.LoginConfig @@ -78,7 +78,7 @@ class OnboardingViewModel @AssistedInject constructor( private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, private val onboardingStore: OnboardingStore, - private val vectorAnalytics: VectorAnalytics + private val analyticsTracker: AnalyticsTracker ) : VectorViewModel(initialState) { @AssistedFactory @@ -466,7 +466,7 @@ class OnboardingViewModel @AssistedInject constructor( private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { viewModelScope.launch { onboardingStore.setUseCase(action.useCase) - vectorAnalytics.updateUserProperties( + analyticsTracker.updateUserProperties( Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()) ) } @@ -476,7 +476,7 @@ class OnboardingViewModel @AssistedInject constructor( private fun resetUseCase() { viewModelScope.launch { onboardingStore.resetUseCase() - vectorAnalytics.updateUserProperties( + analyticsTracker.updateUserProperties( Identity(ftueUseCaseSelection = null) ) } From c4ac03949c34f778aa79fe062029693357bc6b19 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 13:07:57 +0000 Subject: [PATCH 05/77] persisting the use case at the point of session creation - this ensures we have a unique session or account id to store the selection against in case we support multi account in the future --- .../onboarding/OnboardingViewModel.kt | 19 +++++++------------ .../onboarding/OnboardingViewState.kt | 2 ++ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 3e4ea68317..07a7933d4b 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -464,22 +464,14 @@ class OnboardingViewModel @AssistedInject constructor( } private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { - viewModelScope.launch { - onboardingStore.setUseCase(action.useCase) - analyticsTracker.updateUserProperties( - Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()) - ) - } + setState { copy(useCase = action.useCase) } + analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = action.useCase.toTrackingValue())) _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } private fun resetUseCase() { - viewModelScope.launch { - onboardingStore.resetUseCase() - analyticsTracker.updateUserProperties( - Identity(ftueUseCaseSelection = null) - ) - } + setState { copy(useCase = null) } + analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = null)) } private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { @@ -760,6 +752,9 @@ class OnboardingViewModel @AssistedInject constructor( } private suspend fun onSessionCreated(session: Session) { + awaitState().useCase?.let { useCase -> + onboardingStore.setUseCase(useCase) + } activeSessionHolder.setActiveSession(session) authenticationService.reset() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt index fd25f3901e..d05a8294f6 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt @@ -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, From f44ccd739ebba3a8914fd44c53577ef9eff1577f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 13:50:51 +0000 Subject: [PATCH 06/77] storing an retrieving the use case selection by user - will enable multi account support in the future --- .../vector/app/core/extensions/DataStore.kt | 30 +++++++++++++++++++ .../im/vector/app/features/MainActivity.kt | 10 +++---- .../onboarding/OnboardingViewModel.kt | 2 +- .../onboarding/store/OnboardingStore.kt | 21 ++++++------- 4 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/extensions/DataStore.kt diff --git a/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt b/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt new file mode 100644 index 0000000000..72e2408591 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt @@ -0,0 +1,30 @@ +/* + * 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.core.extensions + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit + +suspend fun DataStore.removeKeysWithPrefix(prefix: String) { + edit { preferences -> + val keysToRemove = preferences.asMap().keys.filter { key -> key.name.startsWith(prefix) } + keysToRemove.forEach { + preferences.remove(it) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 89ce4a0545..98a967e925 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -151,7 +151,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity // Just do the local cleanup Timber.w("Account deactivated, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true) + doLocalCleanup(clearPreferences = true, userId = session.myUserId) startNextActivityAndFinish() } } @@ -165,14 +165,14 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } Timber.w("SIGN_OUT: success, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true) + doLocalCleanup(clearPreferences = true, userId = session.myUserId) startNextActivityAndFinish() } } args.clearCache -> { lifecycleScope.launch { session.clearCache() - doLocalCleanup(clearPreferences = false) + doLocalCleanup(clearPreferences = false, userId = session.myUserId) session.startSyncing(applicationContext) startNextActivityAndFinish() } @@ -185,7 +185,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, userId: String) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -195,7 +195,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() - onboardingStore.clear() + onboardingStore.clear(userId) } withContext(Dispatchers.IO) { // On BG thread diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 07a7933d4b..530f2e62a5 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -753,7 +753,7 @@ class OnboardingViewModel @AssistedInject constructor( private suspend fun onSessionCreated(session: Session) { awaitState().useCase?.let { useCase -> - onboardingStore.setUseCase(useCase) + onboardingStore.setUseCase(userId = session.myUserId, useCase) } activeSessionHolder.setActiveSession(session) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt index cc4d324aa0..96ad6ec050 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt @@ -22,6 +22,7 @@ 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.core.extensions.removeKeysWithPrefix import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first import javax.inject.Inject @@ -35,25 +36,25 @@ private val Context.dataStore: DataStore by preferencesDataStore(na class OnboardingStore @Inject constructor( private val context: Context ) { - private val useCaseKey = stringPreferencesKey("use_case") - - suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> - preferences[useCaseKey]?.let { FtueUseCase.from(it) } + suspend fun readUseCase(userId: String) = context.dataStore.data.first().let { preferences -> + preferences[userId.toUseCaseKey()]?.let { FtueUseCase.from(it) } } - suspend fun setUseCase(useCase: FtueUseCase) { + suspend fun setUseCase(userId: String, useCase: FtueUseCase) { context.dataStore.edit { settings -> - settings[useCaseKey] = useCase.persistableValue + settings[userId.toUseCaseKey()] = useCase.persistableValue } } - suspend fun resetUseCase() { + suspend fun resetUseCase(userId: String) { context.dataStore.edit { settings -> - settings.remove(useCaseKey) + settings.remove(userId.toUseCaseKey()) } } - suspend fun clear() { - context.dataStore.edit { it.clear() } + suspend fun clear(userId: String) { + context.dataStore.removeKeysWithPrefix(userId) } + + private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case") } From b91b9cb973a3c603c05bdf9fc59e8a22dc4d8326 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 14:25:17 +0000 Subject: [PATCH 07/77] lifting analytics extension to the common package --- .../analytics/extensions/IdentityExt.kt | 29 +++++++++++++++++++ .../app/features/onboarding/FtueUseCase.kt | 11 ------- .../onboarding/OnboardingViewModel.kt | 1 + 3 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt diff --git a/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt b/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt new file mode 100644 index 0000000000..d87769fd36 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt @@ -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 + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index 23bea3eb99..952612b43f 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -16,8 +16,6 @@ package im.vector.app.features.onboarding -import im.vector.app.features.analytics.plan.Identity - enum class FtueUseCase(val persistableValue: String) { FRIENDS_FAMILY("friends_family"), TEAMS("teams"), @@ -28,12 +26,3 @@ enum class FtueUseCase(val persistableValue: String) { fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue } } } - -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 - } -} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 530f2e62a5..9c3743f372 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -37,6 +37,7 @@ 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 From 73b80b1c7d7fb901ff91f87faafc11ab18b96515 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 25 Jan 2022 17:03:13 +0000 Subject: [PATCH 08/77] providing the onboarding store via session extension - avoids needing to know about the user id for each read/write --- .../im/vector/app/core/extensions/Session.kt | 3 +++ .../im/vector/app/features/MainActivity.kt | 14 ++++++----- .../onboarding/OnboardingViewModel.kt | 5 ++-- .../onboarding/store/OnboardingStore.kt | 23 ++++++++++--------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index 90b08ef92b..b350e0041c 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -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.onboarding.store.OnboardingStore 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.onboardingStore(context: Context) = OnboardingStore(context, myUserId) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 98a967e925..25d0323577 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -28,6 +28,7 @@ import dagger.hilt.android.AndroidEntryPoint 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.onboardingStore import im.vector.app.core.extensions.startSyncing import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.deleteAllFiles @@ -99,7 +100,6 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity @Inject lateinit var pinLocker: PinLocker @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var vectorAnalytics: VectorAnalytics - @Inject lateinit var onboardingStore: OnboardingStore override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -145,13 +145,15 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity startNextActivityAndFinish() return } + + val onboardingStore = session.onboardingStore(this) when { args.isAccountDeactivated -> { lifecycleScope.launch { // Just do the local cleanup Timber.w("Account deactivated, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true, userId = session.myUserId) + doLocalCleanup(clearPreferences = true, onboardingStore) startNextActivityAndFinish() } } @@ -165,14 +167,14 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } Timber.w("SIGN_OUT: success, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true, userId = session.myUserId) + doLocalCleanup(clearPreferences = true, onboardingStore) startNextActivityAndFinish() } } args.clearCache -> { lifecycleScope.launch { session.clearCache() - doLocalCleanup(clearPreferences = false, userId = session.myUserId) + doLocalCleanup(clearPreferences = false, onboardingStore) session.startSyncing(applicationContext) startNextActivityAndFinish() } @@ -185,7 +187,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean, userId: String) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, onboardingStore: OnboardingStore) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -195,7 +197,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() - onboardingStore.clear(userId) + onboardingStore.clear() } withContext(Dispatchers.IO) { // On BG thread diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 9c3743f372..72379f17ea 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -32,6 +32,7 @@ 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.onboardingStore import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash @@ -45,7 +46,6 @@ import im.vector.app.features.login.LoginMode import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ServerType import im.vector.app.features.login.SignMode -import im.vector.app.features.onboarding.store.OnboardingStore import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain @@ -78,7 +78,6 @@ class OnboardingViewModel @AssistedInject constructor( private val stringProvider: StringProvider, private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, - private val onboardingStore: OnboardingStore, private val analyticsTracker: AnalyticsTracker ) : VectorViewModel(initialState) { @@ -754,7 +753,7 @@ class OnboardingViewModel @AssistedInject constructor( private suspend fun onSessionCreated(session: Session) { awaitState().useCase?.let { useCase -> - onboardingStore.setUseCase(userId = session.myUserId, useCase) + session.onboardingStore(applicationContext).setUseCase(useCase) } activeSessionHolder.setActiveSession(session) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt index 96ad6ec050..3193c68f98 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt @@ -25,7 +25,6 @@ import androidx.datastore.preferences.preferencesDataStore import im.vector.app.core.extensions.removeKeysWithPrefix import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first -import javax.inject.Inject private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_onboarding") @@ -33,27 +32,29 @@ private val Context.dataStore: DataStore by preferencesDataStore(na * Local storage for: * - messaging use case (Enum/String) */ -class OnboardingStore @Inject constructor( - private val context: Context +class OnboardingStore constructor( + private val context: Context, + private val myUserId: String ) { - suspend fun readUseCase(userId: String) = context.dataStore.data.first().let { preferences -> - preferences[userId.toUseCaseKey()]?.let { FtueUseCase.from(it) } + + suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> + preferences[myUserId.toUseCaseKey()]?.let { FtueUseCase.from(it) } } - suspend fun setUseCase(userId: String, useCase: FtueUseCase) { + suspend fun setUseCase(useCase: FtueUseCase) { context.dataStore.edit { settings -> - settings[userId.toUseCaseKey()] = useCase.persistableValue + settings[myUserId.toUseCaseKey()] = useCase.persistableValue } } - suspend fun resetUseCase(userId: String) { + suspend fun resetUseCase() { context.dataStore.edit { settings -> - settings.remove(userId.toUseCaseKey()) + settings.remove(myUserId.toUseCaseKey()) } } - suspend fun clear(userId: String) { - context.dataStore.removeKeysWithPrefix(userId) + suspend fun clear() { + context.dataStore.removeKeysWithPrefix(myUserId) } private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case") From bc373917b53dfebd0fde905dcd7916fd3e19ac53 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 26 Jan 2022 09:15:44 +0000 Subject: [PATCH 09/77] making the onboarding store a userId scoped generic store instead --- .../vector/app/core/extensions/DataStore.kt | 30 ------------------- .../im/vector/app/core/extensions/Session.kt | 4 +-- .../im/vector/app/features/MainActivity.kt | 10 +++---- .../onboarding/OnboardingViewModel.kt | 4 +-- .../VectorSessionStore.kt} | 22 +++++++------- 5 files changed, 19 insertions(+), 51 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/core/extensions/DataStore.kt rename vector/src/main/java/im/vector/app/features/{onboarding/store/OnboardingStore.kt => session/VectorSessionStore.kt} (69%) diff --git a/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt b/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt deleted file mode 100644 index 72e2408591..0000000000 --- a/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.core.extensions - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit - -suspend fun DataStore.removeKeysWithPrefix(prefix: String) { - edit { preferences -> - val keysToRemove = preferences.asMap().keys.filter { key -> key.name.startsWith(prefix) } - keysToRemove.forEach { - preferences.remove(it) - } - } -} diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index b350e0041c..87ed51522f 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -21,7 +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.onboarding.store.OnboardingStore +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 @@ -78,4 +78,4 @@ fun Session.cannotLogoutSafely(): Boolean { !sharedSecretStorageService.isRecoverySetup()) } -fun Session.onboardingStore(context: Context) = OnboardingStore(context, myUserId) +fun Session.vectorStore(context: Context) = VectorSessionStore(context, myUserId) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 25d0323577..33b735551c 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -28,8 +28,8 @@ import dagger.hilt.android.AndroidEntryPoint 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.onboardingStore 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 @@ -37,11 +37,11 @@ import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.notifications.NotificationDrawerManager -import im.vector.app.features.onboarding.store.OnboardingStore 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 @@ -146,7 +146,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity return } - val onboardingStore = session.onboardingStore(this) + val onboardingStore = session.vectorStore(this) when { args.isAccountDeactivated -> { lifecycleScope.launch { @@ -187,7 +187,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean, onboardingStore: OnboardingStore) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, vectorSessionStore: VectorSessionStore) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -197,7 +197,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() - onboardingStore.clear() + vectorSessionStore.clear() } withContext(Dispatchers.IO) { // On BG thread diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 72379f17ea..8097e90206 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -32,7 +32,7 @@ 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.onboardingStore +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 @@ -753,7 +753,7 @@ class OnboardingViewModel @AssistedInject constructor( private suspend fun onSessionCreated(session: Session) { awaitState().useCase?.let { useCase -> - session.onboardingStore(applicationContext).setUseCase(useCase) + session.vectorStore(applicationContext).setUseCase(useCase) } activeSessionHolder.setActiveSession(session) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt similarity index 69% rename from vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt rename to vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt index 3193c68f98..bd0b7e2cca 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt +++ b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.onboarding.store +package im.vector.app.features.session import android.content.Context import androidx.datastore.core.DataStore @@ -22,40 +22,38 @@ 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.core.extensions.removeKeysWithPrefix import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first -private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_onboarding") - /** * Local storage for: * - messaging use case (Enum/String) */ -class OnboardingStore constructor( +class VectorSessionStore constructor( private val context: Context, - private val myUserId: String + myUserId: String ) { + private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_$myUserId") + private val useCaseKey = stringPreferencesKey("use_case") + suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> - preferences[myUserId.toUseCaseKey()]?.let { FtueUseCase.from(it) } + preferences[useCaseKey]?.let { FtueUseCase.from(it) } } suspend fun setUseCase(useCase: FtueUseCase) { context.dataStore.edit { settings -> - settings[myUserId.toUseCaseKey()] = useCase.persistableValue + settings[useCaseKey] = useCase.persistableValue } } suspend fun resetUseCase() { context.dataStore.edit { settings -> - settings.remove(myUserId.toUseCaseKey()) + settings.remove(useCaseKey) } } suspend fun clear() { - context.dataStore.removeKeysWithPrefix(myUserId) + context.dataStore.edit { settings -> settings.clear() } } - - private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case") } From cbdeb54fdbd687561f043ad807d0620c8a722523 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 26 Jan 2022 18:43:09 +0000 Subject: [PATCH 10/77] using md5 hash of the userId to normalise the usage as a key --- .../java/im/vector/app/features/session/VectorSessionStore.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt index bd0b7e2cca..ce85eeeb98 100644 --- a/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt +++ b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt @@ -24,6 +24,7 @@ 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: @@ -34,7 +35,7 @@ class VectorSessionStore constructor( myUserId: String ) { - private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_$myUserId") + private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_${myUserId.md5()}") private val useCaseKey = stringPreferencesKey("use_case") suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> From f1288e53b163e340d23df194a65a220d06fe73ee Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 26 Jan 2022 06:48:46 +0000 Subject: [PATCH 11/77] Translated using Weblate (Czech) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/ --- vector/src/main/res/values-cs/strings.xml | 47 +++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index c54fb52153..dbbb6d5238 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -611,7 +611,7 @@ Zmínit Zobrazit seznam relací Toto je náhled místnosti. Interakce s místností byla vypnuta. - Vykázání uživatele jej vykopne z této místnosti a zamezí, aby opět vstoupili. + Vykázání uživatele jej odebere z této místnosti a zamezí, aby opět vstoupil. Důvod Opravdu chcete pozvat uživatele %s do této konverzace\? "%1$s, " @@ -930,7 +930,7 @@ Ukázat potvrzení o přečtení Klepněte na potvrzení o přečtení pro podrobnosti. Ukázat události příchodů a odchodů - Nemá vliv na pozvánky, nakopnutí a zákazy. + Pozvánky, odebrání a vykázání nejsou dotčeny. Ukázat události účtu Zahrnout avatar a změny veřejného jména. Vibrovat při zmínce uživatele @@ -2386,7 +2386,7 @@ %d vteřin Zobrazit stavové události účastníků v místnosti - Zahrnuje události pozvat/vstoupit/opustit/vykopnout/vykázat a změny avatara/veřejného jména. + Zahrnuje události pozvat/vstoupit/opustit/odebrat/vykázat a změny avatara/veřejného jména. Hlasování Reagoval(a): %s Výsledek ověření @@ -3010,7 +3010,7 @@ Zobrazit a aktualizovat role potřebné ke změně různých částí prostoru. Oprávnění prostoru Zrušením vykázání uživateli umožní znovu se připojit do prostoru. - Vykázáním uživatele z tohoto prostoru vykopnete a zabráníte mu v dalším připojení. + Vykázání uživatele z tohoto prostoru ho odebere a zamezí, aby opět vstoupil. 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. @@ -3072,8 +3072,8 @@ Konečný výsledek na základě %1$d hlasů - Nikdo nehlasoval - %1$d hlasů. Hlasujte pro zobrazení výsledků + %1$d hlas. Hlasujte pro zobrazení výsledků + %1$d hlasy. Hlasujte pro zobrazení výsledků %1$d hlasů. Hlasujte pro zobrazení výsledků @@ -3120,7 +3120,40 @@ Přepsat barvu přezdívky Již mám účet Zlepšete týmovou komunikaci. - Spojte se s kýmkoli. + Bezpečené zasílání zpráv. Máte vše pod kontrolou. Vlastněte své konverzace. + Sdílet polohu + Zobrazit polohy uživatele na časové ose + Po zapnutí budete moci odeslat svou polohu do libovolné místnosti + Povolit sdílení polohy + Otevřít v + ${app_name} nemohl získat přístup k vaší poloze. Zkuste to prosím později. + ${app_name} nemohl získat přístup k vaší poloze + Sdílet polohu + Sdílet polohu + Poloha + Sdílet polohu + Výsledky se zobrazí až po ukončení hlasování + Uzavřené hlasování + Hlasující vidí výsledky ihned po hlasování + Otevřené hlasování + Typ hlasování + UPRAVIT HLASOVÁNÍ + Upravit hlasování + Nikdo nehlasoval + Chybně nakonfigurovaná úroveň důvěryhodnosti + Šifrování je špatně nakonfigurováno + Obnovit šifrování + Š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. + Kontaktujte správce, aby obnovil šifrování do funkčního stavu. + Šifrování bylo špatně nakonfigurováno. + Sdíleli svou polohu + Vytvořit účet + Zasílání zpráv pro váš tým. + Koncové šifrování bez potřeby telefonního čísla. Žádné reklamy ani vytěžování dat. + Můžete si vybrat, kde budou vaše konverzace uloženy, a získat tak kontrolu a nezávislost. Připojeno přes Matrix. + Bezpečná a nezávislá komunikace, která vám poskytne stejnou úroveň soukromí jako osobní rozhovor u vás doma. + Poloha + Šifrování bylo špatně nakonfigurováno, takže nelze odesílat zprávy. Kliknutím otevřete nastavení. \ No newline at end of file From 0d29859fab0c34dc8e9059eef17be615f1f4b3eb Mon Sep 17 00:00:00 2001 From: artevaeckt Date: Thu, 27 Jan 2022 11:51:58 +0000 Subject: [PATCH 12/77] Translated using Weblate (German) Currently translated at 99.2% (2732 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/de/ --- vector/src/main/res/values-de/strings.xml | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml index b4616a338c..4d92def9b1 100644 --- a/vector/src/main/res/values-de/strings.xml +++ b/vector/src/main/res/values-de/strings.xml @@ -3058,4 +3058,28 @@ Endgültiges Ergebnis basiert auf %1$d Stimmen Element verbindet Datenschutz mit tollen Features. + Verschlüsselung wiederherstellen + Standort freigeben + Standort freigeben + Standort freigeben + Standort freigeben + Standortfreigabe aktivieren + Öffnen mit + ${app_name} konnte nicht auf deinen Standort zugreifen. Bitte versuche es später noch einmal. + ${app_name} konnte nicht auf deinen Standort zugreifen + Standort + Ergebnisse werden erst angezeigt, wenn du die Umfrage beendest + Geschlossene Umfrage + Ergebnisse werden direkt nach Stimmabgabe angezeigt + Offene Umfrage + Umfragetyp + UMFRAGE BEARBEITEN + Umfrage bearbeiten + Keine Stimmen abgegeben + Konto erstellen + Nachrichtenaustausch für dein Team. + Ende-zu-Ende-verschlüsselt und ohne Telefonnummer nutzbar. Keine Werbung oder Datenerfassung. + Wähle wo deine Gespräche liegen, für Kontrolle und Unabhängigkeit. Verbunden mit Matrix. + Sichere und unabhängige Kommunikation, die für die gleiche Vertraulichkeit sorgt, wie ein Gespräch von Angesicht zu Angesicht in deinem eigenen Zuhause. + Standort \ No newline at end of file From 8f288990de239bfbc9b3b7bd6d2d1cb91f869feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 25 Jan 2022 20:46:49 +0000 Subject: [PATCH 13/77] Translated using Weblate (Estonian) Currently translated at 99.9% (2750 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/et/ --- vector/src/main/res/values-et/strings.xml | 39 +++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index 2799218362..77f854f73b 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -1381,7 +1381,7 @@ Näita kõikide sõnumite ajatempleid Lugemisteatisele klõpsimine kuvab detailsema loendi. Näita jututubadega liitumised ja neist lahkumised - Siia alla ei kuulu kutsed, müksamised ja suhtluskeelud. + Siia alla ei kuulu kutsed, eemaldamised ja suhtluskeelud. Näita kontoga seotud sündmusi Sealhulgas tunnuspildi ja kuvatava nime muutusi. Kasutaja mainimisel anna väringa märku @@ -2342,7 +2342,7 @@ %d sekundit Näita jututoa liikmete olekusündmusi - Sealhulgas kutsumisi, liitumisi, lahkumisi, müksamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi. + Sealhulgas kutsumisi, liitumisi, lahkumisi, eemaldamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi. Küsitlus Reageeris: %s Verifitseerimise tulemus @@ -3061,7 +3061,40 @@ Asenda hüüdnime värvid Mul on kasutajakonto juba olemas Lase tiimidel vabalt tegutseda. - Suhtle kellega soovid. + Turvaline sõnumivahetus. Sul on kontroll oma andmete üle. Vestlused, mida sa tegelikult ka omad. + Jaga asukohta + Kuva ajajoonel kasutaja asukohti + Kui see seadistus on kasutusel, siis sa saad oma asukohta jagada igas jututoas + Luba asukohta jagada + Ava muu rakendusega + ${app_name} ei saanud asukohta tuvastada. Palun proovi hiljem uuesti. + ${app_name} ei saanud asukohta tuvastada + Jaga asukohta + Jaga asukohta + Asukoht + Jaga asukohta + Tulemusi kuvame vaid siis, kui küsitlus on lõppenud + Küsitlus on lõppenud + Osalejad näevad tulemusi peale oma valiku salvestamist + Ava küsitlus + Küsitluse tüüp + MUUDA KÜSITLUST + Muuda küsitlust + Hääletanuid ei ole + Seadmete usaldusseosed on valesti seadistatud + Krüptimise seadistustes on viga + Taasta krüptimine + Kui soovid krüptimist töökorda saada, siis võta ühendust serveri haldajaga. + Krüptimise seadistustes on viga. + Jagas oma asukohta + Loo kasutajakonto + Sõnumisuhtlus sinu tiimi või kogukonna jaoks. + Tagatud on andmete läbiv krüptimine ning oma telefoninumbrit ei pea sa jagama. Pole reklaame ega sinu andmete kogumist. + Sa ise valid serveri, kus sinu vestlusi hoitakse ning sellega tagadki kontrolli oma andmete üle. Lahendus põhineb Matrix\'i võrgul. + Turvaline ja sõltumatu suhtluslahendus, mis tagab sama privaatsuse, kui omavaheline vestlus sinu kodus. + Asukoht + Krüptimise seadistustes on viga ja sa ei saa sõnumeid saata. Seadistuste avamiseks klõpsi siin. + 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. \ No newline at end of file From 4a799786f514bee67cc325895f22824ea6f5b251 Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Tue, 25 Jan 2022 21:35:47 +0000 Subject: [PATCH 14/77] Translated using Weblate (Persian) Currently translated at 99.3% (2733 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/ --- vector/src/main/res/values-fa/strings.xml | 26 +++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index 84a6a6d5ca..a3782df603 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -815,7 +815,7 @@ لغو نادیده‌گیری اشاره نمایش فهرست نشست - انسداد کاربر، او را از این اتاق اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند. + انسداد کاربر، او را از این اتاق برداشته و از پیوستن دوباره‌اش جلوگیری می‌کند. دلیل تنظیمات پیام‌ها @@ -1002,7 +1002,7 @@ \nمدیرهای یکپارچگی، داده‌های پیکربندی را دریافت کرده و می‌توانند از طرف شما ابزارک‌ها را تغییر داده، دعوت‌های اتاق فرستاده و سطوح قدرت را تنظیم کنند. نمایش برچسب زمانی برای تمامی پیام‌ها نمایش برچسب‌های زمانی در قالب ۱۲ساعته - شامل رویدادهای دعوت/پیوستن/ترک/اخراج/تحریم و تغییرهای چهرک/نام نمایشی. + شامل رویدادهای دعوت/پیوستن/ترک/برداشتن/تحریم و تغییرهای چهرک/نام نمایشی. پشتیبان امن مدیریت برپایی پشتیبان امن @@ -2956,7 +2956,7 @@ دیدن و به‌روز رسانی نقش‌های لازم برای تغییر بخش‌های مختلف فضا. اجازه‌های فضا لغو تحریم کاربر، اجازهٔ پیوستن دوباره‌اش به فضا را می‌دهد. - تحریم کاربر، او را از این فضا اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند. + تحریم کاربر، او را از این فضا برداشته و از پیوستن دوباره‌اش جلوگیری می‌کند. اخراج کاربر، از این فضا برش می‌دارد. \n \nبرای پیش‌گیری از پیوستن دوباره، باید تحریمش کنید. @@ -3059,7 +3059,25 @@ سامانه‌تان هنگام‌ مواجهه با خطای ناتوانی در رمزگشایی، گزارش‌ها را به صورت خودکار خواهد فرستاد گزارش خودکار خطاهای رمزگشایی. از پیش حساب دارم - ارتباط با هرکسی. + پیام‌رسانی امن. شما کنترل می‌کنید. صاحب گفت‌وگوهایتان باشید. + هم‌رسانی مکان + به کار انداختن هم‌رسانی مکان + گشودن با + هم‌رسانی مکان + هم‌رسانی مکان + مکان + هم‌رسانی مکان + نظرسنجی بسته + گشودن نظرسنجی + گونهٔ نظرسنجی + ویرایش نظرسنجی + ویرایش نظرسنجی + رأیی داده نشده + بازیابی رمزنگاری + مکانش را هم‌رسانی کرد + ایجاد حساب + پیام‌رسانی برای گروهتان. + مکان \ No newline at end of file From 2f96b6ef559c6aab99f7b13cbec81bfe03d39172 Mon Sep 17 00:00:00 2001 From: Paragoumba Date: Wed, 26 Jan 2022 21:00:31 +0000 Subject: [PATCH 15/77] Translated using Weblate (French) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/ --- vector/src/main/res/values-fr/strings.xml | 41 ++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml index 5a7b47622f..3ea79e0a9d 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -3017,7 +3017,7 @@ Résultat final sur la base de %1$d votes - Aucun vote exprimé + %1$d vote exprimé. Votez pour voir les résultats %1$d votes exprimés. Votez pour voir les résultats @@ -3040,8 +3040,8 @@ La politique de votre serveur d’accueil Politique de ${app_name} Vous pouvez désactiver ceci à tout moment dans les paramètres - Nous ne partageons pas d’information avec des tierces parties - Nous n’enregistrons ou ne profilons aucune donnée du compte + Nous ne partageons aucune information avec des tiers + Nous n’enregistrons ou ne profilons aucune donnée du compte ici Aidez nous à identifier les problèmes et améliorer Element en envoyant des rapports d’usage 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 @@ Outrepasser la couleur du pseudo J’ai déjà un compte Être vicTeams, ce n’est pas Slack vous voulez. - Se connecter avec qui vous voulez. + Messagerie sécurisée. Vous êtes aux commandes. Contrôlez vos conversations. Vous n’êtes pas autorisé à rejoindre ce salon + Choisissez où vos conversations sont stockées, vous avez le contrôle et êtes indépendant. Connecté via Matrix. + Le chiffrement a été mal configuré ce qui vous empêche d’envoyer des messages. Veuillez contacter un administrateur pour remettre le chiffrement en état de marche. + Partager la localisation + Afficher les localisations de l\'utilisateur dans le temps + Une fois activé il vous sera possible d\'envoyer votre localisation dans n\'importe quel salon + Activer le partage de localisation + Ouvrir avec + ${app_name} n\'a pas pu accéder à votre localisation. Veuillez réessayer plus tard. + ${app_name} n\'a pas pu accéder à votre localisation + Partager la localisation + Partager la localisation + Localisation + Partager la localisation + Les résultats ne sont dévoilés que quand vous terminez le sondage + Sondage fermé + Les votants voient les résultats dès qu\'ils ont votés + Ouvrir le sondage + Type de sondage + ÉDITER LE SONDAGE + Éditer le sondage + Aucun vote exprimé + Niveau de confiance mal configuré + Le chiffrement est mal configuré + Restaurer le Chiffrement + Veuillez contacter un administrateur pour reconfigurer le chiffrement. + Le chiffrement a été mal configuré. + On partagé leur localisation + Créer un compte + Messagerie pour votre équipe. + 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. + Communication indépendante et sécurisée qui vous donne le même niveau d\'intimité qu\'une discussion face-à-face dans votre maison. + Localisation + Le chiffrement a été mal configuré ce qui vous empêche d\'envoyer des messages. Cliquez pour ouvrir les paramètres. \ No newline at end of file From 5319f0359276e97cb673d605ecf514d51df7b20e Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 26 Jan 2022 00:33:05 +0000 Subject: [PATCH 16/77] Translated using Weblate (Indonesian) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/id/ --- vector/src/main/res/values-in/strings.xml | 35 ++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 21dfefa374..62145bf64e 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -3006,7 +3006,40 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Ubah warna nama tampilan Saya sudah punya akun Bebaskan. - Hubung dengan siapa pun. + Perpesanan yang aman. Anda dalam kendali. Miliki percakapan Anda. + Bagikan lokasi + Tampilkan lokasi pengguna di linimasa + Setelah diaktifkan Anda akan dapat mengirim lokasi Anda ke ruangan apa saja + Aktifkan pembagian lokasi + Buka dengan + ${app_name} tidak dapat mengakses lokasi Anda. Silakan coba lagi nanti. + ${app_name} tidak dapat mengakses lokasi Anda + Bagikan lokasi + Bagikan lokasi + Lokasi + Bagikan lokasi + Hasil akan ditampilkan ketika Anda mengakhiri poll-nya + Poll tertutup + Pemberi suara akan melihat hasilnya ketika telah memberikan suara + Buka poll + Tipe poll + EDIT POLL + Edit poll + Tidak ada suara + Tingkat kepercayaan dikonfigurasi dengan salah + Enkripsi dikonfigurasi dengan salah + Pulihkan Enkripsi + Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid. + Enkripsi telah dikonfigurasi dengan salah. + Membagikan lokasinya + Buat akun + Perpesanan untuk tim Anda. + Terenkripsi secara ujung-ke-ujung dan tidak memerlukan nomor telepon. Tidak ada iklan atau penambangan data. + Anda pilih di mana percakapan Anda disimpan, memberikan Anda kendali dan kebebasan. Terhubung via Matrix. + Komunikasi aman dan independen yang memberikan tingkat privasi yang sama seperti percakapan wajah-ke-wajah di dalam rumah Anda sendiri. + Lokasi + Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Klik untuk membuka pengaturan. + Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid. \ No newline at end of file From 2c50cd16b1578e56fa55b6747dec5fd2ecdea5b6 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 26 Jan 2022 11:09:23 +0000 Subject: [PATCH 17/77] Translated using Weblate (Italian) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/it/ --- vector/src/main/res/values-it/strings.xml | 45 ++++++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml index d581a97f0b..221a996e0c 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -846,7 +846,7 @@ %d utente %d utenti - Bandire l\'utente lo espellerà dalla stanza e gli impedirà di rientrare. + Bandire l\'utente lo rimuoverà da questa stanza e gli impedirà di rientrare. %d nuovo messaggio %d nuovi messaggi @@ -1081,7 +1081,7 @@ Mostra le conferme di lettura Clicca sulle conferme di lettura per vederne l\'elenco dettagliato. Mostra entrata ed uscita degli utenti - Inviti, butta fuori e ban non vengono considerati. + Inviti, rimozioni e ban non vengono considerati. Mostra i cambiamenti degli account Includi cambiamenti dell\'avatar e del nome visualizzato. Password @@ -2333,7 +2333,7 @@ %d secondi Mostra eventi di stato dei membri della stanza - Includi eventi di invito/entrata/uscita/kick/ban e modifiche di avatar/nome. + Includi eventi di invito/entrata/uscita/rimozione/ban e modifiche di avatar/nome. Sondaggio Reagito con: %s Verifica conclusa @@ -2946,7 +2946,7 @@ Seleziona i ruoli necessari per cambiare varie parti dello spazio Vedi e aggiorna i ruoli necessari per cambiare varie parti dello spazio. Riammettere l\'utente gli permetterà di rientrare nello spazio. - Bandire l\'utente lo espellerà da questo spazio e gli impedirà di rientrare. + Bandire l\'utente lo rimuoverà da questo spazio e gli impedirà di rientrare. buttando fuori l\'utente verrà rimosso da questo spazio. \n \nPer impedire che possa rientrare, dovresti invece bandirlo. @@ -3006,7 +3006,7 @@ Risultato finale basato su %1$d voti - Nessun voto + %1$d voto. Vota per vedere i risultati %1$d voti. Vota per vedere i risultati @@ -3051,7 +3051,40 @@ Sovrascrivi colore nick Ho già un account Riduci il carico ai team. - Connettiti con chiunque. + Messaggistica sicura. Tu hai il controllo. Prendi il controllo delle tue conversazioni. + Condividi posizione + Mostra le posizioni dell\'utente nella linea temporale + Una volta attivata, potrai inviare la tua posizione in qualsiasi stanza + Attiva condivisione posizione + Apri con + ${app_name} non ha potuto rilevare la tua posizione. Riprova più tardi. + ${app_name} non ha potuto rilevare la tua posizione + Condividi posizione + Condividi posizione + Posizione + Condividi posizione + I risultati verranno rivelati solo quando termini il sondaggio + Sondaggio chiuso + I votanti vedono i risultati appena avranno votato + Apri sondaggio + Tipo sondaggio + MODIFICA SONDAGGIO + Modifica sondaggio + Nessun voto + Livello di fiducia configurato male + La crittografia è configurata male + Reimposta crittografia + Contatta un amministratore per reimpostare la crittografia ad uno stato valido. + La crittografia è stata configurata male. + Ha condiviso la sua posizione + Crea account + Messaggistica per la tua squadra. + Crittografia end-to-end e nessun numero di telefono richiesto. Niente pubblicità o raccolta di dati. + Scegli dove vengono tenute le tue conversazioni, dandoti controllo e indipendenza. Connesso via Matrix. + Comunicazioni sicure e indipendenti che ti danno lo stesso livello di riservatezza di una conversazione faccia a faccia in casa tua. + Posizione + La crittografia è stata configurata male, perciò non puoi inviare messaggi. Clicca per aprire le impostazioni. + La crittografia è stata configurata male, perciò non puoi inviare messaggi. Contatta l\'amministratore per reimpostare la crittografia ad uno stato valido. \ No newline at end of file From f4af074148877da462eec06a49d5f51e8cbdf023 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Wed, 26 Jan 2022 14:16:38 +0000 Subject: [PATCH 18/77] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/ --- vector/src/main/res/values-pt-rBR/strings.xml | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index 513709c0fd..228b61cb6f 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -819,7 +819,7 @@ Tirar vídeo Lista de Grupos Chamar - Banir usuária(o) vai expulsá-la(o) desta sala e preveni-la(o) de se juntar de novo. + Banir usuária(o) vai removê-la(o) desta sala e preveni-la(o) de se juntar de novo. Todas as mensagens (barulhento) Todas as mensagens Menções somente @@ -1118,7 +1118,7 @@ Mostrar recibos de leitura Clique nos recibos de leitura para uma lista detalhada. Mostrar eventos de juntar-se e sair - Convites, expulsões e bans são desafetados. + Convites, remoções e bans são desafetados. Mostrar eventos de conta Inclui mudanças de avatar e nome de exibição. Conexão no Background @@ -2334,7 +2334,7 @@ %d segundo %d segundos - Inclui eventos de convite/juntar-se/saiu/expulsão/ban e mudanças de avatar/nome de exibição. + Inclui eventos de convidar/juntar-se/saiu/remover/ban e mudanças de avatar/nome de exibição. Emails e números de telefone Gerenciar emails e números de telefone linkados a sua conta Matrix Código @@ -2956,7 +2956,7 @@ Veja e atualize os papéis requeridos para mudar várias partes do espaço. Permissões de espaço Desbanir usuária(o) vai permiti-la(o) se juntar ao espaço de novo. - Banir usuária(o) vai expulsá-la(o) deste espaço e preveni-la de se juntar de novo. + Banir usuária(o) vai removê-la(o) deste espaço e preveni-la de se juntar de novo. 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. @@ -3061,7 +3061,40 @@ Sobrepor cor de nick Eu já tenho uma conta Dê liberdade a times. - Conecte-se com qualquer pessoa. + Mensageria segura. Você está em controle. Tenha posse de suas conversas. + Compartilhar localização + Render localizações de usuária(o) na timeline + Uma vez habilitada você vai ser capaz de enviar sua localização a qualquer sala + Habilitar compartilhamento de localização + Abrir com + ${app_name} não pôde acessar sua localização. Por favor tente de novo mais tarde. + ${app_name} não pôde acessar sua localização + Compartilhar localização + Compartilhar localização + Localização + Compartilhar localização + Resultados são somente revelados quando você termina a sondagem + Sondagem fechada + Votantes veem resultados assim que elas(es) têm votado + Sondagem aberta + Tipo de sondagem + EDITAR SONDAGEM + Editar sondagem + Nenhum voto lançado + Nível de confiança malconfigurado + Encriptação está malconfigurada + Restaurar Encriptação + Por favor contacte um(a) admin para restaurar encriptação a um estado válido. + Compartilhou a localização dela(e) + Localização + Encriptação tem sido malconfigurada. + Criar conta + Mensageria para seu time. + Encriptado ponta-a-ponta e nenhum número de telefone requerido. Sem publicidade ou datamining. + Escolha onde suas conversas são mantidas, dando-lhe controle e independência. Conectado via Matrix. + Comunicação segura e independente que lhe dá o mesmo nível de privacidade que conversa face-a-face em sua própria casa. + Encriptação tem sido malconfigurada então você não pode enviar mensagens. Clique para abrir configurações. + 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. \ No newline at end of file From 6ceb2c48242e41eae80792943bb916c3ec53f9c5 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 25 Jan 2022 22:46:08 +0000 Subject: [PATCH 19/77] Translated using Weblate (Albanian) Currently translated at 99.4% (2738 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sq/ --- vector/src/main/res/values-sq/strings.xml | 53 ++++++++++++++++++----- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index a31673925d..d93331777f 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -500,7 +500,7 @@ ID Përdoruesi, Emër ose email-i Përmendje Shfaq Listë Sesionesh - Dëbimi i përdoruesit do ta përzërë atë nga kjo dhomë dhe do t’i pengojë atij rihyrjen. + Dëbimi i përdoruesit do ta heqë atë nga kjo dhomë dhe do t’i pengojë atij rihyrjen. Arsye Jeni i sigurt se doni të ftohet %s te kjo fjalosje? "%1$s, " @@ -1048,7 +1048,7 @@ Parashihini lidhjet brenda fjalosjes, kur shërbyesi juaj home e mbulon këtë veçori. Lejojuni përdoruesve të tjerë të dinë se po shtypni. 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. - Nuk prek ftesat, përzëniet dhe dëbimet. + Nuk prek ftesat, heqjet dhe dëbimet. ${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ë. ${app_name}-i grumbullon të dhëna analitike anonime që të na lejojë ta përmirësojmë aplikacionin. Ju lutemi, aktivizoni analizat që të na ndihmoni të përmirësojmë ${app_name}-in. @@ -2201,7 +2201,7 @@ %d sekonda Shfaq akte gjendjeje përdoruesish të dhomës - Përfshin akte ftimi/pjesëmarrjeje/ikjeje/përzënieje/dëbimi dhe ndryshime emri avatari/shfaqjeje. + Përfshin akte ftimi/pjesëmarrjeje/ikjeje/heqjeje/dëbimi dhe ndryshime emri avatari/shfaqjeje. Kopjeruajtje e Sigurt Administroni Ujdisni Kopjeruajtje të Sigurt @@ -2867,8 +2867,8 @@ %1$s Prekeni që të kthehet Thirrje aktive (%1$s) · - Thirrje aktive - %1$d thirrje aktive + Thirrje aktive · + %1$d thirrje aktive · Lidhja dështoi S’ka përgjigje @@ -2913,7 +2913,7 @@ Do të braktisni krejt dhomat dhe hapësirat te %s. Braktis krejt dhomat dhe hapësirat Jeni i sigurt se doni të dilni nga \'%s\? - Zbulim (%S) + Zbulim (%s) Përfundoje ujdisjen Ftoni me email, gjeni kontakte, etj… Përfundo ujdisjen e zbulimit. @@ -2945,7 +2945,7 @@ Të shohë dhe përditësojë role të domosdoshëm për të ndryshuar anë të ndryshme të hapësirës. Leje hapësire Heqja e dëbimit përdoruesit do t’i lejojë të marrë pjesë sërish në hapësirë. - Dëbimi i përdoruesit do ta përzërë atë nga kjo hapësirë dhe do t’i pengojë atij rihyrjen. + Dëbimi i përdoruesit do ta heqë atë nga kjo hapësirë dhe do t’i pengojë atij rihyrjen. 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. @@ -3032,8 +3032,8 @@ Ndihmoni të përmirësohet Element-in Aktivizoje - S’ka vota të hedhura - %1$d votë e hedhur. Votoni, që të shihni përfundimet + %1$d votë e hedhur. Votoni, që të shihni përfundimet + %1$d vota të hedhur. Votoni, që të shihni përfundimet Që ndryshimi të hyjë në fuqi, rinisni aplikacionin. Aktivizo elementë LaTeX për matematikë @@ -3048,7 +3048,40 @@ Raporto Vetvetiu Gabime Shfshehtëzimi. Anashkalo ngjyrë nofke Kam tashmë një llogari - Lidhuni me këdo. + Shkëmbim i siguruar mesazhesh. Kontrollin e keni ju. Jini zot i bisedave tuaja. + Jepe vendndodhjen + Aktivizoni dhënie vendndodhjeje + Jepe vendndodhjen + Jepe vendndodhjen + Vendndodhje + Jepe vendndodhjen + Përfundimet shfaqen vetëm kur përfundoni anketimin + Anketim i mbyllur + Votuesit do të shohin përfundime sapo të kenë votuar + Anketim i hapur + Lloj anketimi + Krijoni llogari + Shkëmbim mesazhesh për ekipin tuaj. + I fshehtëzuar skaj më skaj dhe pa u dashur numër telefoni. Pa reklama, apo shfrytëzim të dhënash. + Zgjidhni ku mbahen bisedat tuaja, duke ju dhënë kontroll dhe pavarësi. Të lidhur përmes Matrix-i. + 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. + Trego vendndodhje përdoruesi në rrjedhën kohore + Pasi të aktivizohet, do të jeni në gjendje të dërgoni vendndodhjen tuaj në çfarëdo dhome + Hape me + ${app_name} s’njohu dot vendndodhjen tuaj. Ju lutemi, riprovoni më vonë. + ${app_name} s’mësoi dot vendndodhjen tuaj + PËRPUNONI ANKETIMIN + Përpunoni anketimin + S’janë hedhur vota + Shkallë besueshmërie e formësuar gabim + Fshehtëzimi është formësuar gabim + Riktheni Fshehtëzimin + Ju lutemi, lidhuni me një nga përgjegjësit, që ta ktheni fshehtëzimin në një gjendje të vlefshme. + Fshehtëzimi është formësuar gabim. + Tregoi vendndodhjen e vet + Vendndodhje + Fshehtëzimi është formësuar keq, që të mos dërgoni mesazhe. Klikoni që të hapni rregullimet. + 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. \ No newline at end of file From d31818c1b39a731e718a9fd8d1c3e6d3ae15bb73 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 25 Jan 2022 21:29:50 +0000 Subject: [PATCH 20/77] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 37 +++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 0a6261d2c1..c9e8372cc9 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -3105,7 +3105,7 @@ Точно видалити опитування\? Ви не зможете відновити опитування після видалення. Видалити опитування Голосування завершене - Голос надіслано + Проголосовано Завершити опитування Люди більше не зможуть голосувати, і будуть показані остаточні результати опитування. Завершити це опитування\? @@ -3163,7 +3163,40 @@ Замінити колір псевдоніма Я вже маю обліковий запис Удоскональте спілкування в команді. - З\'єднуйтеся з будь-ким. + Захищене спілкування. Ви контролюєте все. Володійте своїми розмовами. + Поділитися місцеперебуванням + Зображувати місцеперебування користувача у стрічці + Після увімкнення ви зможете надіслати своє місцеперебування до будь-якої кімнати + Увімкнути надсилання місцеперебування + Відкрити за допомогою + ${app_name} не може отримати доступ до вашого місцеперебування. Спробуйте пізніше. + ${app_name} не може отримати доступ до вашого місцеперебування + Поділитися місцеперебуванням + Поділитися місцеперебуванням + Місцеперебування + Поділитися місцеперебуванням + Результати можна переглянути лише після завершення опитування + Закрите опитування + Усі, хто проголосує, побачать результати одразу після голосування + Відкрити опитування + Тип опитування + РЕДАГУВАТИ ОПИТУВАННЯ + Редагувати опитування + Немає голосових бесід + Неправильне налаштування рівня довіри + Шифрування неправильно налаштовано + Відновити шифрування + Зв\'яжіться з адміністратором для відновлення роботи шифрування. + Шифрування неправильно налаштовано. + Поділилися своїм місцеперебуванням + Створити обліковий запис + Спілкування вашої команди. + З наскрізним шифруванням і без вимоги номера телефону. Без реклами чи аналізу даних. + Оберіть де спілкуватись, що дасть вам контроль і незалежність. Під\'єднано через Matrix. + Захищене та незалежне спілкування, яке дає вам такий самий рівень приватності, як розмова віч-на-віч у вашому власному домі. + Місцеперебування + Шифрування було налаштовано неправильно, тому ви не можете надсилати повідомлення. Торкніться, щоб відкрити налаштування. + Шифрування було налаштовано неправильно, тому ви не можете надсилати повідомлення. Зверніться до адміністратора, щоб відновити роботу шифрування. \ No newline at end of file From bce49e04cd94a1759510d5380edf21c827d1b391 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 26 Jan 2022 02:11:17 +0000 Subject: [PATCH 21/77] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/ --- vector/src/main/res/values-zh-rTW/strings.xml | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml index 9a8e90f751..3dcf132f92 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -542,7 +542,7 @@ 提及 顯示工作階段清單 您確定想要邀請 %s 到聊天室? - 封鎖使用者將會把他們從此聊天室中踢除,並避免他們再次加入。 + 封鎖使用者將會把他們從此聊天室中移除,並避免他們再次加入。 透過 ID 邀請 裝置上的聯絡人(%d) 使用者目錄 (%s) @@ -1046,7 +1046,7 @@ 顯示讀取回條 點選讀取回條以顯示詳細資料。 顯示加入與離開的活動 - 邀請、踢出與封鎖不受影響。 + 邀請、移除與封鎖不受影響。 顯示帳號活動 包含大頭貼與顯示名稱變動。 密碼 @@ -2298,7 +2298,7 @@ %d 秒 顯示聊天室成員狀態活動 - 包含邀請/加入/離開/踢除/封鎖事件與大頭貼/顯示名稱變更等。 + 包含邀請/加入/離開/移除/封鎖事件與大頭貼/顯示名稱變更等。 投票 反應:%s 驗證結論 @@ -2902,7 +2902,7 @@ 檢視並更新變更空間各部份所需的角色。 空間權限 取消封鎖使用者將讓他們可以再次加入空間。 - 封鎖使用者會將他們踢出此空間並防止他們再次加入。 + 封鎖使用者會將他們自此空間移除並防止他們再次加入。 踢除使用者將會將他們從此空間中移除。 \n \n為了防止他們再加入,您應該封鎖他們。 @@ -3002,7 +3002,40 @@ 覆寫暱稱色彩 我已有一個帳號 減少團隊的懈怠。 - 與任何人聯絡。 + 安全傳送訊息。 您已掌控了您的資料。 擁有您的對話。 + 分享位置 + 在時間軸中繪製使用者位置 + 啟用後,您就能將您的位置傳送至任何聊天室 + 啟用位置分享 + 開啟以 + ${app_name} 無法存取您的位置。請稍後再試。 + ${app_name} 無法存取您的位置 + 分享位置 + 分享位置 + 位置 + 分享位置 + 結果僅在您結束投票後顯示 + 已關閉投票 + 投票者在投票後可以立刻看到投票結果 + 開啟投票 + 投票類型 + 編輯投票 + 編輯投票 + 沒有投票 + 信任層級設定錯誤 + 加密設定錯誤 + 還原加密 + 請聯絡管理員以將加密還原至有效狀態。 + 加密設定錯誤。 + 分享了他們的位置 + 建立帳號 + 為您的團隊傳送訊息。 + 端到端加密,不需要電話號碼。沒有廣告或資料挖礦。 + 選擇保留對話的位置,讓您擁有控制權與獨立性。透過 Matrix 連結。 + 安全且獨立的通訊,為您提供與在家中進行面對面對話相同的隱私等級。 + 位置 + 加密設定錯誤,因此您無法傳送訊息。點擊以開啟設定。 + 加密設定錯誤,因此您無法傳送訊息。請聯絡管理員將加密還原至有效的狀態。 \ No newline at end of file From 090d620cbfb29f0819a4ac0db2c895f9ad08a01d Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Wed, 26 Jan 2022 14:18:10 +0000 Subject: [PATCH 22/77] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/pt_BR/ --- fastlane/metadata/android/pt-BR/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/pt-BR/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/pt-BR/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40103150.txt diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103130.txt b/fastlane/metadata/android/pt-BR/changelogs/40103130.txt new file mode 100644 index 0000000000..6f4975d01c --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103140.txt b/fastlane/metadata/android/pt-BR/changelogs/40103140.txt new file mode 100644 index 0000000000..ab7470b96b --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103150.txt b/fastlane/metadata/android/pt-BR/changelogs/40103150.txt new file mode 100644 index 0000000000..40439e3f94 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103150.txt @@ -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 From f964721d8ef4df14ef807a2aa94601bc75a4cc3f Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 25 Jan 2022 20:45:48 +0000 Subject: [PATCH 23/77] Translated using Weblate (Ukrainian) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/ --- fastlane/metadata/android/uk/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/uk/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/uk/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/uk/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/uk/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/uk/changelogs/40103150.txt diff --git a/fastlane/metadata/android/uk/changelogs/40103130.txt b/fastlane/metadata/android/uk/changelogs/40103130.txt new file mode 100644 index 0000000000..ad0bcda6e6 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/uk/changelogs/40103140.txt b/fastlane/metadata/android/uk/changelogs/40103140.txt new file mode 100644 index 0000000000..355f8add3e --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/uk/changelogs/40103150.txt b/fastlane/metadata/android/uk/changelogs/40103150.txt new file mode 100644 index 0000000000..0d9d702f02 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.15 From 71a7f45ff159de4ecc621f1566bf01c706993a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 25 Jan 2022 20:33:48 +0000 Subject: [PATCH 24/77] Translated using Weblate (Estonian) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/et/ --- fastlane/metadata/android/et/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/et/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/et/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/et/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/et/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/et/changelogs/40103150.txt diff --git a/fastlane/metadata/android/et/changelogs/40103130.txt b/fastlane/metadata/android/et/changelogs/40103130.txt new file mode 100644 index 0000000000..b2c8054559 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/et/changelogs/40103140.txt b/fastlane/metadata/android/et/changelogs/40103140.txt new file mode 100644 index 0000000000..14c34169d0 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/et/changelogs/40103150.txt b/fastlane/metadata/android/et/changelogs/40103150.txt new file mode 100644 index 0000000000..ce86924106 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103150.txt @@ -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 From 79bea3b9eb3973c5bcd9d07e25bd08083c590ac1 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 26 Jan 2022 11:12:16 +0000 Subject: [PATCH 25/77] Translated using Weblate (Italian) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/it/ --- fastlane/metadata/android/it-IT/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/it-IT/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/it-IT/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/it-IT/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/it-IT/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/it-IT/changelogs/40103150.txt diff --git a/fastlane/metadata/android/it-IT/changelogs/40103130.txt b/fastlane/metadata/android/it-IT/changelogs/40103130.txt new file mode 100644 index 0000000000..d113b7ac66 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/it-IT/changelogs/40103140.txt b/fastlane/metadata/android/it-IT/changelogs/40103140.txt new file mode 100644 index 0000000000..b875832368 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/it-IT/changelogs/40103150.txt b/fastlane/metadata/android/it-IT/changelogs/40103150.txt new file mode 100644 index 0000000000..0e7586be19 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103150.txt @@ -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 From 40e70bb4616b11f5c10c806e0e7df862b3fdeaf6 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 26 Jan 2022 01:56:42 +0000 Subject: [PATCH 26/77] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hant/ --- fastlane/metadata/android/zh-TW/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/zh-TW/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/zh-TW/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40103150.txt diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103130.txt b/fastlane/metadata/android/zh-TW/changelogs/40103130.txt new file mode 100644 index 0000000000..e0f9b47e16 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103130.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103140.txt b/fastlane/metadata/android/zh-TW/changelogs/40103140.txt new file mode 100644 index 0000000000..8366a01265 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103140.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103150.txt b/fastlane/metadata/android/zh-TW/changelogs/40103150.txt new file mode 100644 index 0000000000..a6ddd5aa8c --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103150.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.15 From 9b843f39afd06e34f0e4cfdea83fe217469c6006 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 26 Jan 2022 06:26:21 +0000 Subject: [PATCH 27/77] Translated using Weblate (Czech) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/cs/ --- fastlane/metadata/android/cs-CZ/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/cs-CZ/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/cs-CZ/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40103150.txt diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103130.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103130.txt new file mode 100644 index 0000000000..dab96ddd72 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103140.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103140.txt new file mode 100644 index 0000000000..8d07600bc1 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103150.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103150.txt new file mode 100644 index 0000000000..260011b49d --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103150.txt @@ -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 From 0f0ad7ddd36bad68e54b1d873cf8024b2cd8b6b0 Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 26 Jan 2022 00:36:13 +0000 Subject: [PATCH 28/77] Translated using Weblate (Indonesian) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/id/ --- fastlane/metadata/android/id/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/id/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/id/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/id/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/id/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/id/changelogs/40103150.txt diff --git a/fastlane/metadata/android/id/changelogs/40103130.txt b/fastlane/metadata/android/id/changelogs/40103130.txt new file mode 100644 index 0000000000..26a784d62a --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103130.txt @@ -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: diff --git a/fastlane/metadata/android/id/changelogs/40103140.txt b/fastlane/metadata/android/id/changelogs/40103140.txt new file mode 100644 index 0000000000..dfefff307f --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/id/changelogs/40103150.txt b/fastlane/metadata/android/id/changelogs/40103150.txt new file mode 100644 index 0000000000..c46e661d47 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103150.txt @@ -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 From 79b11623b5a852135adcbb918e6daed6c9bcbc91 Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 27 Jan 2022 15:16:46 +0000 Subject: [PATCH 29/77] Call transfer should put other side on hold while the transfer is being selected and unhold when the user cancels selection. --- changelog.d/5081.bugfix | 1 + .../vector/app/features/call/VectorCallActivity.kt | 14 +++++++++++++- .../app/features/call/VectorCallViewActions.kt | 1 + .../app/features/call/VectorCallViewModel.kt | 4 ++++ .../features/call/transfer/CallTransferActivity.kt | 8 +++++++- .../call/transfer/CallTransferViewEvents.kt | 2 +- .../call/transfer/CallTransferViewModel.kt | 8 ++++---- .../app/features/navigation/DefaultNavigator.kt | 8 ++++++-- .../im/vector/app/features/navigation/Navigator.kt | 7 ++++++- 9 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 changelog.d/5081.bugfix diff --git a/changelog.d/5081.bugfix b/changelog.d/5081.bugfix new file mode 100644 index 0000000000..f333c20da8 --- /dev/null +++ b/changelog.d/5081.bugfix @@ -0,0 +1 @@ +Selecting Transfer in a call should immediately put the the other person on hold until the call connects or the Transfer is cancelled. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 22f1fc40a2..6b2fb9db80 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -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 @@ -57,8 +59,10 @@ import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs +import im.vector.app.features.widgets.WidgetActivity import io.github.hyuwah.draggableviewlib.DraggableView import io.github.hyuwah.draggableviewlib.setupDraggable import kotlinx.parcelize.Parcelize @@ -67,7 +71,9 @@ import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxPeerConnectionState import org.matrix.android.sdk.api.session.call.TurnServerResponse +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.call.EndCallReason +import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent import org.webrtc.EglBase import org.webrtc.RendererCommon import timber.log.Timber @@ -518,13 +524,19 @@ class VectorCallActivity : VectorBaseActivity(), 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... diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt index 67aa7bede2..fb39660282 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt @@ -36,5 +36,6 @@ sealed class VectorCallViewActions : VectorViewModelAction { object ToggleCamera : VectorCallViewActions() object ToggleHDSD : VectorCallViewActions() object InitiateCallTransfer : VectorCallViewActions() + object CallTransferSelectionCancelled : VectorCallViewActions() object TransferCall : VectorCallViewActions() } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 5af2b826af..4aca0ea499 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -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() } diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt index 959e96cc4c..0e63316bbe 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt @@ -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() { 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() { } } + private fun handleComplete() { + setResult(Activity.RESULT_OK) + finish() + } + companion object { fun newIntent(context: Context, callId: String): Intent { diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt index fd4c9d672d..a8451e4fb5 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt @@ -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() } diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt index ffc6ff9bc3..de6a5de539 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt @@ -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) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index f66ced3299..8f0b1723a0 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -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, + 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) { diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 775272bd33..451f79c77a 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -149,7 +149,12 @@ interface Navigator { fun openDevTools(context: Context, roomId: String) - fun openCallTransfer(context: Context, callId: String) + fun openCallTransfer( + context: Context, + activityResultLauncher: ActivityResultLauncher, + callId: String + ) + fun openCreatePoll(context: Context, roomId: String, editedEventId: String?, mode: PollMode) From 7242f1c2f14556e221a3a53d21bb6eadf60b51e7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 16:18:40 +0100 Subject: [PATCH 30/77] Fix a crash when disabling the location on the device `LocationListener` does not have default implementation for some methods for Android versions below R --- .../java/im/vector/app/features/location/LocationTracker.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 0c0315cf34..8495d826d9 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -19,16 +19,16 @@ 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 timber.log.Timber import javax.inject.Inject class LocationTracker @Inject constructor( private val context: Context -) : LocationListener { +) : LocationListenerCompat { interface Callback { fun onLocationUpdate(locationData: LocationData) From 3ecc73b42a5ee96fbf7acc35bc2a996efb2d06cf Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 27 Jan 2022 15:19:08 +0000 Subject: [PATCH 31/77] remove line break --- .../src/main/java/im/vector/app/features/navigation/Navigator.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 451f79c77a..d6ca83a8a3 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -155,7 +155,6 @@ interface Navigator { callId: String ) - fun openCreatePoll(context: Context, roomId: String, editedEventId: String?, mode: PollMode) fun openLocationSharing(context: Context, From db3f60b4b1ded40ff3de01e56794d73a12c72901 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 16:23:39 +0100 Subject: [PATCH 32/77] Inform the user when the location provider is disabled. --- .../java/im/vector/app/features/location/LocationTracker.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 8495d826d9..9ccf79de33 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -83,6 +83,10 @@ class LocationTracker @Inject constructor( callback?.onLocationUpdate(location.toLocationData()) } + override fun onProviderDisabled(provider: String) { + callback?.onLocationProviderIsNotAvailable() + } + private fun Location.toLocationData(): LocationData { return LocationData(latitude, longitude, accuracy.toDouble()) } From f449b9954857bc7988fe1ff64e823e2b3c396970 Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 27 Jan 2022 15:24:39 +0000 Subject: [PATCH 33/77] lint --- .../java/im/vector/app/features/call/VectorCallActivity.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 6b2fb9db80..642d259723 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -59,10 +59,8 @@ import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer -import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs -import im.vector.app.features.widgets.WidgetActivity import io.github.hyuwah.draggableviewlib.DraggableView import io.github.hyuwah.draggableviewlib.setupDraggable import kotlinx.parcelize.Parcelize @@ -71,9 +69,7 @@ import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxPeerConnectionState import org.matrix.android.sdk.api.session.call.TurnServerResponse -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.call.EndCallReason -import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent import org.webrtc.EglBase import org.webrtc.RendererCommon import timber.log.Timber @@ -532,7 +528,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } private val callTransferActivityResultLauncher = registerStartForActivityResult { activityResult -> - if(activityResult.resultCode == Activity.RESULT_CANCELED) { + if (activityResult.resultCode == Activity.RESULT_CANCELED) { callViewModel.handle(VectorCallViewActions.CallTransferSelectionCancelled) } } From a5df52ce28d96f3b95ec9c9afa6fbe62ae2d97d5 Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 27 Jan 2022 15:39:47 +0000 Subject: [PATCH 34/77] Update changelog.d/5081.bugfix Co-authored-by: Benoit Marty --- changelog.d/5081.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/5081.bugfix b/changelog.d/5081.bugfix index f333c20da8..6ec6dcc43e 100644 --- a/changelog.d/5081.bugfix +++ b/changelog.d/5081.bugfix @@ -1 +1 @@ -Selecting Transfer in a call should immediately put the the other person on hold until the call connects or the Transfer is cancelled. \ No newline at end of file +Selecting Transfer in a call should immediately put the other person on hold until the call connects or the Transfer is cancelled. \ No newline at end of file From e3242f0debf8556229ac2c02ac9dfb6a9d000364 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 16:40:49 +0100 Subject: [PATCH 35/77] Prevent the dialog from being cancellable, since OK button finish the Activity. --- .../im/vector/app/features/location/LocationSharingFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 900f465f04..b35a72029b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -118,6 +118,7 @@ class LocationSharingFragment @Inject constructor( .setPositiveButton(R.string.ok) { _, _ -> activity?.finish() } + .setCancelable(false) .show() } From e9b9406bf11fd1069cd79a8a290400859e176861 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 21:11:20 +0100 Subject: [PATCH 36/77] Rework the location code - WIP --- .../timeline/helper/LocationPinProvider.kt | 35 +++++-- .../im/vector/app/features/location/Config.kt | 4 +- .../location/LocationPreviewFragment.kt | 37 ++++++- .../location/LocationSharingAction.kt | 2 - .../location/LocationSharingFragment.kt | 86 ++++++++-------- .../location/LocationSharingViewModel.kt | 36 +++++-- .../location/LocationSharingViewState.kt | 4 +- .../app/features/location/LocationTracker.kt | 72 ++++++++------ .../app/features/location/MapTilerMapView.kt | 98 ++++++++++++++----- .../app/features/location/VectorMapView.kt | 32 ------ 10 files changed, 247 insertions(+), 159 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/location/VectorMapView.kt diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt index fe3a7d9007..e92376c44d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt @@ -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(size, size) { override fun onResourceReady(resource: Drawable, transition: Transition?) { - 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 + } } diff --git a/vector/src/main/java/im/vector/app/features/location/Config.kt b/vector/src/main/java/im/vector/app/features/location/Config.kt index 630df16a37..0b3eb015b9 100644 --- a/vector/src/main/java/im/vector/app/features/location/Config.kt +++ b/vector/src/main/java/im/vector/app/features/location/Config.kt @@ -17,5 +17,5 @@ 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 MIN_TIME_TO_UPDATE_LOCATION_MILLIS = 5 * 1_000L // every 5 seconds +const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f diff --git a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt index 6209bf5a4f..641b5910a6 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt @@ -22,19 +22,27 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup 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 locationPinProvider: LocationPinProvider ) : VectorBaseFragment() { private val args: LocationSharingArgs by args() + // Keep a ref to handle properly the onDestroy callback + private var mapView: WeakReference? = null + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationPreviewBinding { return FragmentLocationPreviewBinding.inflate(layoutInflater, container, false) } @@ -42,6 +50,8 @@ class LocationPreviewFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + mapView = WeakReference(views.mapView) + views.mapView.onCreate(savedInstanceState) views.mapView.initialize { if (isAdded) { onMapReady() @@ -49,16 +59,42 @@ class LocationPreviewFragment @Inject constructor( } } + override fun onResume() { + super.onResume() + views.mapView.onResume() + } + override fun onPause() { views.mapView.onPause() 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 { @@ -85,7 +121,6 @@ class LocationPreviewFragment @Inject constructor( 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) } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt index 71101d0612..01319ef6c7 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt @@ -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() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index b35a72029b..2ac8200f60 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -21,29 +21,29 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup 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(), LocationTracker.Callback { - - init { - locationTracker.callback = this - } +) : VectorBaseFragment() { 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? = 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() 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) @@ -122,6 +114,10 @@ class LocationSharingFragment @Inject constructor( .show() } + override fun invalidate() = withState(viewModel) { state -> + views.mapView.render(state) + } + companion object { const val USER_PIN_NAME = "USER_PIN_NAME" } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index b3c97310e1..f4e1fd0281 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -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(initialState) { + private val locationTracker: LocationTracker, + private val locationPinProvider: LocationPinProvider, + private val session: Session +) : VectorViewModel(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 by hiltMavericksViewModelFactory() { + companion object : MavericksViewModelFactory 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) } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt index 2869929b12..7ee5ba00fa 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt @@ -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( diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 9ccf79de33..9d3d506a73 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -27,63 +27,73 @@ import timber.log.Timber import javax.inject.Inject class LocationTracker @Inject constructor( - private val context: Context + context: Context ) : LocationListenerCompat { + private val locationManager = context.getSystemService() + interface Callback { fun onLocationUpdate(locationData: LocationData) fun onLocationProviderIsNotAvailable() } - private var locationManager: LocationManager? = null - var callback: Callback? = null + private var callback: Callback? = null @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) - fun start() { - val locationManager = context.getSystemService() + fun start(callback: Callback?) { + Timber.d("## LocationTracker. start()") + 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 } + + val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + + Timber.d("## LocationTracker. isGpsEnabled: $isGpsEnabled - isNetworkEnabled: $isNetworkEnabled") + + 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 + locationManager.getLastKnownLocation(provider)?.let { lastKnownLocation -> + Timber.d("## LocationTracker. lastKnownLocation") + callback?.onLocationUpdate(lastKnownLocation.toLocationData()) + } + + Timber.d("## LocationTracker. track location using $provider") + locationManager.requestLocationUpdates( + provider, + MIN_TIME_TO_UPDATE_LOCATION_MILLIS, + MIN_DISTANCE_TO_UPDATE_LOCATION_METERS, + this + ) } @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) { + Timber.d("## LocationTracker. onLocationChanged") callback?.onLocationUpdate(location.toLocationData()) } override fun onProviderDisabled(provider: String) { + Timber.d("## LocationTracker. onProviderDisabled: $provider") callback?.onLocationProviderIsNotAvailable() } diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index c64af1ebaa..e22525485e 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -28,34 +28,47 @@ 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: LocationSharingViewState? = 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 + + // TODO Kept only for the bottom sheet usage + fun initialize(onMapReady: () -> Unit) { getMapAsync { map -> map.setStyle(styleUrl) { style -> - this.symbolManager = SymbolManager(this, map, style) - this.map = map - this.style = style + mapRefs = MapRefs( + map, + SymbolManager(this, map, style), + style + ) onMapReady() } } } - override fun addPinToMap(pinId: String, image: Drawable) { - style?.addImage(pinId, image) + // TODO Kept only for the bottom sheet usage + fun addPinToMap(pinId: String, image: Drawable) { + mapRefs?.style?.addImage(pinId, image) } - override fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) { - symbolManager?.create( + // TODO Kept only for the bottom sheet usage + fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) { + mapRefs?.symbolManager?.create( SymbolOptions() .withLatLng(LatLng(latitude, longitude)) .withIconImage(pinId) @@ -63,28 +76,59 @@ class MapTilerMapView @JvmOverloads constructor( ) } - override fun deleteAllPins() { - symbolManager?.deleteAll() + /** + * For location fragments + */ + fun initialize() { + Timber.d("## Location: initialize") + + getMapAsync { map -> + map.setStyle(styleUrl) { style -> + mapRefs = MapRefs( + map, + SymbolManager(this, map, style), + style + ) + pendingState?.let { render(it) } + pendingState = null + } + } } - override fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) { - map?.cameraPosition = CameraPosition.Builder() + fun render(data: LocationSharingViewState) { + val safeMapRefs = mapRefs ?: return Unit.also { + pendingState = data + } + + data.pinDrawable?.let { pinDrawable -> + if (safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) { + safeMapRefs.style.addImage(LocationSharingFragment.USER_PIN_NAME, pinDrawable) + } + } + + data.lastKnownLocation?.let { locationData -> + if (!initZoomDone) { + zoomToLocation(locationData.latitude, locationData.longitude, INITIAL_MAP_ZOOM) + initZoomDone = true + } + + safeMapRefs.symbolManager.create( + SymbolOptions() + .withLatLng(LatLng(locationData.latitude, locationData.longitude)) + .withIconImage(LocationSharingFragment.USER_PIN_NAME) + .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) + ) + } + } + + fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) { + Timber.d("## Location: zoomToLocation") + mapRefs?.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 - } - } - companion object { private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}" } diff --git a/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt b/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt deleted file mode 100644 index 23b59bf99a..0000000000 --- a/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.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) -} From 1f53945031edf96f6a9055c238d5a3a464c05865 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 22:10:19 +0100 Subject: [PATCH 37/77] Rework the location code - WIP --- .../BottomSheetMessagePreviewItem.kt | 29 +++++++--- .../timeline/item/MessageLocationItem.kt | 29 +++++++--- .../location/LocationPreviewFragment.kt | 25 ++++---- .../location/LocationSharingFragment.kt | 4 +- .../location/LocationSharingViewState.kt | 7 +++ .../vector/app/features/location/MapState.kt | 26 +++++++++ .../app/features/location/MapTilerMapView.kt | 58 ++++++------------- 7 files changed, 104 insertions(+), 74 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/location/MapState.kt diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index cdecd2d6c6..37e07a3448 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -34,6 +34,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid 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.MapState import im.vector.app.features.location.MapTilerMapView import im.vector.app.features.media.ImageContentRenderer import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence @@ -99,15 +100,25 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel - 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) - } - } + holder.mapView.initialize() + holder.mapView.render( + MapState( + zoomOnlyOnce = false, + pinLocationData = locationData, + pinId = matrixItem.id, + pinDrawable = null + ) + ) + locationPinProvider?.create(matrixItem.id) { pinDrawable -> + if (holder.view.isAttachedToWindow) { + holder.mapView.render( + MapState( + zoomOnlyOnce = false, + pinLocationData = locationData, + pinId = matrixItem.id, + pinDrawable = pinDrawable + ) + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index 3f030866a5..e5d86d09c7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -24,6 +24,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.onClick import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.location.LocationData +import im.vector.app.features.location.MapState import im.vector.app.features.location.MapTilerMapView @EpoxyModelClass(layout = R.layout.item_timeline_event_base) @@ -56,14 +57,25 @@ abstract class MessageLocationItem : AbsMessageItem( 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) - } + holder.mapView.initialize() + holder.mapView.render( + MapState( + zoomOnlyOnce = false, + pinLocationData = location, + pinId = locationOwnerId, + pinDrawable = null + ) + ) + locationPinProvider?.create(locationOwnerId) { pinDrawable -> + if (holder.view.isAttachedToWindow) { + holder.mapView.render( + MapState( + zoomOnlyOnce = false, + pinLocationData = location, + pinId = locationOwnerId, + pinDrawable = pinDrawable + ) + ) } } } @@ -78,6 +90,5 @@ abstract class MessageLocationItem : AbsMessageItem( companion object { private const val STUB_ID = R.id.messageContentLocationStub - private const val INITIAL_ZOOM = 15.0 } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt index 641b5910a6..01417fdca0 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt @@ -21,6 +21,7 @@ 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 @@ -52,11 +53,8 @@ class LocationPreviewFragment @Inject constructor( mapView = WeakReference(views.mapView) views.mapView.onCreate(savedInstanceState) - views.mapView.initialize { - if (isAdded) { - onMapReady() - } - } + views.mapView.initialize() + loadPinDrawable() } override fun onResume() { @@ -112,17 +110,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) - addPinToMap(userId, pinDrawable) - updatePinLocation(userId, location.latitude, location.longitude) + lifecycleScope.launchWhenResumed { + views.mapView.render( + MapState( + zoomOnlyOnce = true, + pinLocationData = location, + pinId = args.locationOwnerId, + pinDrawable = pinDrawable + ) + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 2ac8200f60..4dff78b5ef 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -39,8 +39,6 @@ class LocationSharingFragment @Inject constructor( 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? = null @@ -115,7 +113,7 @@ class LocationSharingFragment @Inject constructor( } override fun invalidate() = withState(viewModel) { state -> - views.mapView.render(state) + views.mapView.render(state.toMapState()) } companion object { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt index 7ee5ba00fa..f3b937855a 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt @@ -38,3 +38,10 @@ data class LocationSharingViewState( mode = locationSharingArgs.mode ) } + +fun LocationSharingViewState.toMapState() = MapState( + zoomOnlyOnce = true, + pinLocationData = lastKnownLocation, + pinId = LocationSharingFragment.USER_PIN_NAME, + pinDrawable = pinDrawable +) diff --git a/vector/src/main/java/im/vector/app/features/location/MapState.kt b/vector/src/main/java/im/vector/app/features/location/MapState.kt new file mode 100644 index 0000000000..d001457e4f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/MapState.kt @@ -0,0 +1,26 @@ +/* + * 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.graphics.drawable.Drawable + +data class MapState( + val zoomOnlyOnce: Boolean, + val pinLocationData: LocationData? = null, + val pinId: String, + val pinDrawable: Drawable? = null +) diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index e22525485e..be02b84516 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -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 @@ -29,6 +28,7 @@ import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions import com.mapbox.mapboxsdk.style.layers.Property import im.vector.app.BuildConfig import timber.log.Timber +import java.util.concurrent.atomic.AtomicBoolean class MapTilerMapView @JvmOverloads constructor( context: Context, @@ -36,7 +36,7 @@ class MapTilerMapView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : MapView(context, attrs, defStyleAttr) { - private var pendingState: LocationSharingViewState? = null + private var pendingState: MapState? = null data class MapRefs( val map: MapboxMap, @@ -44,43 +44,18 @@ class MapTilerMapView @JvmOverloads constructor( val style: Style ) + private var isInitializing = AtomicBoolean(false) private var mapRefs: MapRefs? = null private var initZoomDone = false - // TODO Kept only for the bottom sheet usage - fun initialize(onMapReady: () -> Unit) { - getMapAsync { map -> - map.setStyle(styleUrl) { style -> - mapRefs = MapRefs( - map, - SymbolManager(this, map, style), - style - ) - onMapReady() - } - } - } - - // TODO Kept only for the bottom sheet usage - fun addPinToMap(pinId: String, image: Drawable) { - mapRefs?.style?.addImage(pinId, image) - } - - // TODO Kept only for the bottom sheet usage - fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) { - mapRefs?.symbolManager?.create( - SymbolOptions() - .withLatLng(LatLng(latitude, longitude)) - .withIconImage(pinId) - .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) - ) - } - /** * For location fragments */ fun initialize() { - Timber.d("## Location: initialize") + Timber.d("## Location: initialize $isInitializing") + if (isInitializing.getAndSet(true)) { + return + } getMapAsync { map -> map.setStyle(styleUrl) { style -> @@ -95,20 +70,21 @@ class MapTilerMapView @JvmOverloads constructor( } } - fun render(data: LocationSharingViewState) { + fun render(state: MapState) { val safeMapRefs = mapRefs ?: return Unit.also { - pendingState = data + pendingState = state } - data.pinDrawable?.let { pinDrawable -> - if (safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) { + state.pinDrawable?.let { pinDrawable -> + if (safeMapRefs.style.isFullyLoaded && + safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) { safeMapRefs.style.addImage(LocationSharingFragment.USER_PIN_NAME, pinDrawable) } } - data.lastKnownLocation?.let { locationData -> - if (!initZoomDone) { - zoomToLocation(locationData.latitude, locationData.longitude, INITIAL_MAP_ZOOM) + state.pinLocationData?.let { locationData -> + if (!initZoomDone || !state.zoomOnlyOnce) { + zoomToLocation(locationData.latitude, locationData.longitude) initZoomDone = true } @@ -121,11 +97,11 @@ class MapTilerMapView @JvmOverloads constructor( } } - fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) { + private fun zoomToLocation(latitude: Double, longitude: Double) { Timber.d("## Location: zoomToLocation") mapRefs?.map?.cameraPosition = CameraPosition.Builder() .target(LatLng(latitude, longitude)) - .zoom(zoom) + .zoom(INITIAL_MAP_ZOOM) .build() } From 55ed7373f513ef8023dc618c217eb8415d27e004 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 22:20:53 +0100 Subject: [PATCH 38/77] Rework the location code - WIP --- .../im/vector/app/features/location/MapTilerMapView.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index be02b84516..8b64cf79d3 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -76,9 +76,10 @@ class MapTilerMapView @JvmOverloads constructor( } state.pinDrawable?.let { pinDrawable -> - if (safeMapRefs.style.isFullyLoaded && - safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) { - safeMapRefs.style.addImage(LocationSharingFragment.USER_PIN_NAME, pinDrawable) + if (!safeMapRefs.style.isFullyLoaded || + safeMapRefs.style.getImage(state.pinId) == null) { + safeMapRefs.symbolManager.deleteAll() + safeMapRefs.style.addImage(state.pinId, pinDrawable) } } @@ -91,7 +92,7 @@ class MapTilerMapView @JvmOverloads constructor( safeMapRefs.symbolManager.create( SymbolOptions() .withLatLng(LatLng(locationData.latitude, locationData.longitude)) - .withIconImage(LocationSharingFragment.USER_PIN_NAME) + .withIconImage(state.pinId) .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) ) } From 26c0fee56ddcf4376643b15940200b394eccd4c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 22:27:43 +0100 Subject: [PATCH 39/77] Add a loader waiting for the user location to be known --- .../app/features/location/LocationSharingFragment.kt | 2 ++ vector/src/main/res/layout/fragment_location_sharing.xml | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 4dff78b5ef..c18a3540d3 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -20,6 +20,7 @@ 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 @@ -114,6 +115,7 @@ class LocationSharingFragment @Inject constructor( override fun invalidate() = withState(viewModel) { state -> views.mapView.render(state.toMapState()) + views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null } companion object { diff --git a/vector/src/main/res/layout/fragment_location_sharing.xml b/vector/src/main/res/layout/fragment_location_sharing.xml index f9a37a6241..ad418f3e1c 100644 --- a/vector/src/main/res/layout/fragment_location_sharing.xml +++ b/vector/src/main/res/layout/fragment_location_sharing.xml @@ -48,4 +48,13 @@ android:textColor="?colorPrimary" android:textStyle="bold" /> + + \ No newline at end of file From c07b09c727c2a75f9e40e0880c712c2016cb9ee6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jan 2022 23:11:05 +0000 Subject: [PATCH 40/77] Bump libphonenumber from 8.12.41 to 8.12.42 Bumps [libphonenumber](https://github.com/google/libphonenumber) from 8.12.41 to 8.12.42. - [Release notes](https://github.com/google/libphonenumber/releases) - [Changelog](https://github.com/google/libphonenumber/blob/master/making-metadata-changes.md) - [Commits](https://github.com/google/libphonenumber/compare/v8.12.41...v8.12.42) --- updated-dependencies: - dependency-name: com.googlecode.libphonenumber:libphonenumber dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 57f40d1889..a4102f7441 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -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' diff --git a/vector/build.gradle b/vector/build.gradle index 1c65850a00..8792f526d4 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -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 From 0f8c3bcc17e39828459ae8c790886cd90dcd5ab0 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 28 Jan 2022 14:17:07 +0300 Subject: [PATCH 41/77] Try to get location by using all available providers. --- .../app/features/location/LocationTracker.kt | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 9d3d506a73..133819ba7a 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -55,29 +55,28 @@ class LocationTracker @Inject constructor( Timber.d("## LocationTracker. isGpsEnabled: $isGpsEnabled - isNetworkEnabled: $isNetworkEnabled") - val provider = when { - isGpsEnabled -> LocationManager.GPS_PROVIDER - isNetworkEnabled -> LocationManager.NETWORK_PROVIDER - else -> { - callback?.onLocationProviderIsNotAvailable() - Timber.v("## LocationTracker. There is no location provider available") - return - } - } + locationManager.allProviders + .takeIf { it.isNotEmpty() } + ?.forEach { provider -> + Timber.d("## LocationTracker. track location using $provider") - // Send last known location without waiting location updates - locationManager.getLastKnownLocation(provider)?.let { lastKnownLocation -> - Timber.d("## LocationTracker. lastKnownLocation") - callback?.onLocationUpdate(lastKnownLocation.toLocationData()) - } + // Send last known location without waiting location updates + locationManager.getLastKnownLocation(provider)?.let { lastKnownLocation -> + Timber.d("## LocationTracker. lastKnownLocation") + callback?.onLocationUpdate(lastKnownLocation.toLocationData()) + } - Timber.d("## LocationTracker. track location using $provider") - locationManager.requestLocationUpdates( - provider, - MIN_TIME_TO_UPDATE_LOCATION_MILLIS, - MIN_DISTANCE_TO_UPDATE_LOCATION_METERS, - this - ) + 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]) From 22b64a3e9ad0567e5435a54045fe7cdea5f59cf1 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 28 Jan 2022 12:05:12 +0000 Subject: [PATCH 42/77] resetting text futures when updating recycled text --- .../features/home/room/detail/timeline/item/MessageTextItem.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt index 1794f04c2a..e5ac321d90 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt @@ -109,6 +109,7 @@ abstract class MessageTextItem : AbsMessageItem() { val textFuture = PrecomputedTextCompat.getTextFuture(message, TextViewCompat.getTextMetricsParams(this), null) setTextFuture(textFuture) } else { + setTextFuture(null) text = message } } From 4026ddb34fb7fc84b611c92182c12585e23273e8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 28 Jan 2022 15:07:51 +0300 Subject: [PATCH 43/77] Fix multiple pin rendering. --- .../java/im/vector/app/features/location/LocationTracker.kt | 5 ----- .../java/im/vector/app/features/location/MapTilerMapView.kt | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 133819ba7a..ff66baf5ca 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -50,11 +50,6 @@ class LocationTracker @Inject constructor( return } - val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) - val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) - - Timber.d("## LocationTracker. isGpsEnabled: $isGpsEnabled - isNetworkEnabled: $isNetworkEnabled") - locationManager.allProviders .takeIf { it.isNotEmpty() } ?.forEach { provider -> diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index 8b64cf79d3..8ef72dfdff 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -78,7 +78,6 @@ class MapTilerMapView @JvmOverloads constructor( state.pinDrawable?.let { pinDrawable -> if (!safeMapRefs.style.isFullyLoaded || safeMapRefs.style.getImage(state.pinId) == null) { - safeMapRefs.symbolManager.deleteAll() safeMapRefs.style.addImage(state.pinId, pinDrawable) } } @@ -89,6 +88,7 @@ class MapTilerMapView @JvmOverloads constructor( initZoomDone = true } + safeMapRefs.symbolManager.deleteAll() safeMapRefs.symbolManager.create( SymbolOptions() .withLatLng(LatLng(locationData.latitude, locationData.longitude)) From 2eeeefdf49b4a127e0b8f3067708bc648e0dc8fc Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 28 Jan 2022 12:11:54 +0000 Subject: [PATCH 44/77] adding changelog entry --- changelog.d/5091.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5091.bugfix diff --git a/changelog.d/5091.bugfix b/changelog.d/5091.bugfix new file mode 100644 index 0000000000..cf55a6dd10 --- /dev/null +++ b/changelog.d/5091.bugfix @@ -0,0 +1 @@ +Fixing crashes when quickly scrolling or restoring the room timeline \ No newline at end of file From 0c2116a95d1c952bdb96c418b0c5f9dd5b4060ac Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 28 Jan 2022 12:48:26 +0000 Subject: [PATCH 45/77] using espresso long click instead of barista helper and waiting for reaction before interacting - the non barista long pressing works for the menu opening, maybe this will fix the CI --- .../java/im/vector/app/ui/robot/RoomDetailRobot.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt index ebf5fdf23d..da57b105d7 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt @@ -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()) } } From d6f0e065aa37e393d1978e2b595ce424dac4b7fb Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 28 Jan 2022 12:49:28 +0000 Subject: [PATCH 46/77] fixing missing when branches warning --- vector/src/androidTest/java/im/vector/app/EspressoExt.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt index 59ad122f36..ef76a9d1f3 100644 --- a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt +++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -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 + } } } } From f66bd4ffe9e44a0826f9412239ebfc6cd756d7e6 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 28 Jan 2022 13:20:56 +0000 Subject: [PATCH 47/77] waiting for the emoji to become visible instead of a hardcoded sleep --- .../java/im/vector/app/ui/robot/MessageMenuRobot.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt index 934c6c76a1..5973dc3473 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt @@ -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 { + waitUntilViewVisible(withId(R.id.emojiRecyclerView)) + waitUntilViewVisible(withText("😀")) + } clickListItem(R.id.emojiRecyclerView, 4) autoClosed = true } From 50279e31832216aa39b787f7fd370702623638fc Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 28 Jan 2022 18:51:28 +0300 Subject: [PATCH 48/77] Use static map image in timeline. --- .../timeline/factory/MessageItemFactory.kt | 9 +++- .../timeline/item/MessageLocationItem.kt | 49 +++++++++---------- .../im/vector/app/features/location/Config.kt | 29 ++++++++++- .../location/LocationSharingFragment.kt | 3 +- .../app/features/location/MapTilerMapView.kt | 9 +--- .../item_timeline_event_location_stub.xml | 33 ++++++------- vector/src/main/res/values/strings.xml | 1 + 7 files changed, 77 insertions(+), 56 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index eab7621d14..b0bdef5636 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -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 @@ -127,7 +128,8 @@ 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 resources: Resources) { // TODO inject this properly? private var roomId: String = "" @@ -207,11 +209,16 @@ class MessageItemFactory @Inject constructor( } } + val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60) + val height = dimensionConverter.dpToPx(200) + return MessageLocationItem_() .attributes(attributes) .locationData(locationData) .userId(informationData.senderId) .locationPinProvider(locationPinProvider) + .mapWidth(width) + .mapHeight(height) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(mapCallback) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index e5d86d09c7..4c32d933ca 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -17,15 +17,17 @@ 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.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.location.INITIAL_MAP_ZOOM_IN_TIMELINE import im.vector.app.features.location.LocationData -import im.vector.app.features.location.MapState -import im.vector.app.features.location.MapTilerMapView +import im.vector.app.features.location.getStaticMapUrl @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageLocationItem : AbsMessageItem() { @@ -46,6 +48,12 @@ abstract class MessageLocationItem : AbsMessageItem( @EpoxyAttribute var locationPinProvider: LocationPinProvider? = null + @EpoxyAttribute + var mapWidth: Int = 1200 + + @EpoxyAttribute + var mapHeight: Int = 800 + override fun bind(holder: Holder) { super.bind(holder) renderSendState(holder.mapViewContainer, null) @@ -53,39 +61,28 @@ abstract class MessageLocationItem : AbsMessageItem( val location = locationData ?: return val locationOwnerId = userId ?: return - holder.clickableMapArea.onClick { + holder.mapViewContainer.onClick { callback?.onMapClicked() } - holder.mapView.initialize() - holder.mapView.render( - MapState( - zoomOnlyOnce = false, - pinLocationData = location, - pinId = locationOwnerId, - pinDrawable = null - ) - ) + GlideApp.with(holder.staticMapImageView) + .load(getStaticMapUrl(location.latitude, location.longitude, INITIAL_MAP_ZOOM_IN_TIMELINE, mapWidth, mapHeight)) + .apply(RequestOptions.centerCropTransform()) + .into(holder.staticMapImageView) + locationPinProvider?.create(locationOwnerId) { pinDrawable -> - if (holder.view.isAttachedToWindow) { - holder.mapView.render( - MapState( - zoomOnlyOnce = false, - pinLocationData = location, - pinId = locationOwnerId, - pinDrawable = pinDrawable - ) - ) - } + GlideApp.with(holder.staticMapPinImageView) + .load(pinDrawable) + .into(holder.staticMapPinImageView) } } override fun getViewType() = STUB_ID class Holder : AbsMessageItem.Holder(STUB_ID) { - val mapViewContainer by bind(R.id.mapViewContainer) - val mapView by bind(R.id.mapView) - val clickableMapArea by bind(R.id.clickableMapArea) + val mapViewContainer by bind(R.id.mapViewContainer) + val staticMapImageView by bind(R.id.staticMapImageView) + val staticMapPinImageView by bind(R.id.staticMapPinImageView) } companion object { diff --git a/vector/src/main/java/im/vector/app/features/location/Config.kt b/vector/src/main/java/im/vector/app/features/location/Config.kt index 0b3eb015b9..9b42c6d9b3 100644 --- a/vector/src/main/java/im/vector/app/features/location/Config.kt +++ b/vector/src/main/java/im/vector/app/features/location/Config.kt @@ -16,6 +16,33 @@ package im.vector.app.features.location -const val INITIAL_MAP_ZOOM = 15.0 +import im.vector.app.BuildConfig + +const val MAP_STYLE_URL = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}" +private const val STATIC_MAP_IMAGE_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 + +fun getStaticMapUrl(latitude: Double, + longitude: Double, + zoom: Double, + width: Int, + height: Int): String { + return buildString { + append(STATIC_MAP_IMAGE_URL) + append(longitude) + append(",") + append(latitude) + append(",") + append(zoom) + append("/") + append(width) + append("x") + append(height) + append(".png?key=") + append(BuildConfig.mapTilerKey) + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index c18a3540d3..900d70e825 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -35,8 +35,7 @@ import javax.inject.Inject /** * We should consider using SupportMapFragment for a out of the box lifecycle handling */ -class LocationSharingFragment @Inject constructor( -) : VectorBaseFragment() { +class LocationSharingFragment @Inject constructor() : VectorBaseFragment() { private val viewModel: LocationSharingViewModel by fragmentViewModel() diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index 8ef72dfdff..4ff8c2ad4d 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -26,7 +26,6 @@ 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 import java.util.concurrent.atomic.AtomicBoolean @@ -58,7 +57,7 @@ class MapTilerMapView @JvmOverloads constructor( } getMapAsync { map -> - map.setStyle(styleUrl) { style -> + map.setStyle(MAP_STYLE_URL) { style -> mapRefs = MapRefs( map, SymbolManager(this, map, style), @@ -102,11 +101,7 @@ class MapTilerMapView @JvmOverloads constructor( Timber.d("## Location: zoomToLocation") mapRefs?.map?.cameraPosition = CameraPosition.Builder() .target(LatLng(latitude, longitude)) - .zoom(INITIAL_MAP_ZOOM) + .zoom(INITIAL_MAP_ZOOM_IN_PREVIEW) .build() } - - companion object { - private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}" - } } diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml index b2f68b2fc3..3d5f856a6f 100644 --- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml @@ -6,30 +6,25 @@ android:layout_height="wrap_content" app:cardCornerRadius="8dp"> - - + android:contentDescription="@string/a11y_static_map_image" /> - - - + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 22c890eb01..378b8d7cbf 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3711,6 +3711,7 @@ Share location Location Share location + Map Share location ${app_name} could not access your location ${app_name} could not access your location. Please try again later. From 2dc52da30c82cce9855ed037548f6a10d32adad8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 28 Jan 2022 20:48:53 +0300 Subject: [PATCH 49/77] Use static map image in bottom sheet. --- .../BottomSheetMessagePreviewItem.kt | 53 ++++++++++--------- .../im/vector/app/features/location/Config.kt | 1 + .../item_bottom_sheet_message_preview.xml | 28 +++++++--- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index 37e07a3448..8805c07109 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -17,25 +17,28 @@ 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.INITIAL_MAP_ZOOM_IN_TIMELINE import im.vector.app.features.location.LocationData -import im.vector.app.features.location.MapState -import im.vector.app.features.location.MapTilerMapView +import im.vector.app.features.location.getStaticMapUrl import im.vector.app.features.media.ImageContentRenderer import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence import org.matrix.android.sdk.api.util.MatrixItem @@ -82,6 +85,12 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel - if (holder.view.isAttachedToWindow) { - holder.mapView.render( - MapState( - zoomOnlyOnce = false, - pinLocationData = locationData, - pinId = matrixItem.id, - pinDrawable = pinDrawable - ) - ) + if (locationData == null) { + holder.body.isVisible = true + holder.mapViewContainer.isVisible = false + } else { + holder.body.isVisible = false + holder.mapViewContainer.isVisible = true + GlideApp.with(holder.staticMapImageView) + .load(getStaticMapUrl(locationData!!.latitude, locationData!!.longitude, INITIAL_MAP_ZOOM_IN_TIMELINE, mapWidth, mapHeight)) + .apply(RequestOptions.centerCropTransform()) + .into(holder.staticMapImageView) + + locationPinProvider?.create(matrixItem.id) { pinDrawable -> + GlideApp.with(holder.staticMapPinImageView) + .load(pinDrawable) + .into(holder.staticMapPinImageView) } } } @@ -135,6 +138,8 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel(R.id.bottom_sheet_message_preview_body_details) val timestamp by bind(R.id.bottom_sheet_message_preview_timestamp) val imagePreview by bind(R.id.bottom_sheet_message_preview_image) - val mapView by bind(R.id.bottom_sheet_message_preview_location) + val mapViewContainer by bind(R.id.mapViewContainer) + val staticMapImageView by bind(R.id.staticMapImageView) + val staticMapPinImageView by bind(R.id.staticMapPinImageView) } } diff --git a/vector/src/main/java/im/vector/app/features/location/Config.kt b/vector/src/main/java/im/vector/app/features/location/Config.kt index 9b42c6d9b3..eb5f1ac3b7 100644 --- a/vector/src/main/java/im/vector/app/features/location/Config.kt +++ b/vector/src/main/java/im/vector/app/features/location/Config.kt @@ -44,5 +44,6 @@ fun getStaticMapUrl(latitude: Double, append(height) append(".png?key=") append(BuildConfig.mapTilerKey) + append("&attribution=bottomleft") } } diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml index 95e6975803..5dddfaa2c8 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml @@ -103,18 +103,32 @@ tools:text="1080 x 1024 - 43s - 12kB" tools:visibility="visible" /> - + tools:visibility="visible"> + + + + + From e0ac8ee8b8dc01e1612144856dfabefad062dcd8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jan 2022 18:23:31 +0100 Subject: [PATCH 50/77] No need for an extra FrameLayout --- .../timeline/item/MessageLocationItem.kt | 6 ++-- .../item_timeline_event_location_stub.xml | 30 ++++++++----------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index 4c32d933ca..619d799ec5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.detail.timeline.item -import android.widget.FrameLayout import android.widget.ImageView import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass @@ -56,12 +55,12 @@ abstract class MessageLocationItem : AbsMessageItem( override fun bind(holder: Holder) { super.bind(holder) - renderSendState(holder.mapViewContainer, null) + renderSendState(holder.view, null) val location = locationData ?: return val locationOwnerId = userId ?: return - holder.mapViewContainer.onClick { + holder.view.onClick { callback?.onMapClicked() } @@ -80,7 +79,6 @@ abstract class MessageLocationItem : AbsMessageItem( override fun getViewType() = STUB_ID class Holder : AbsMessageItem.Holder(STUB_ID) { - val mapViewContainer by bind(R.id.mapViewContainer) val staticMapImageView by bind(R.id.staticMapImageView) val staticMapPinImageView by bind(R.id.staticMapPinImageView) } diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml index 3d5f856a6f..316470b5f1 100644 --- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml @@ -6,25 +6,19 @@ android:layout_height="wrap_content" app:cardCornerRadius="8dp"> - + android:layout_height="200dp" + android:contentDescription="@string/a11y_static_map_image" /> - - - - + From eff6942f82019953efd09f80215c2f688b7445a2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jan 2022 18:56:09 +0100 Subject: [PATCH 51/77] Use a MaterialCarView --- .../main/res/layout/item_bottom_sheet_message_preview.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml index 5dddfaa2c8..e40760e046 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml @@ -104,15 +104,17 @@ tools:visibility="visible" /> - - + From 2ce389456281b3c1c5890acc0482893792c21678 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jan 2022 22:46:58 +0100 Subject: [PATCH 52/77] Create a UrlMapProvider for a better handling of RTL languages, and build the URLs in the controllers --- .../src/main/res/values-ldrtl/bools.xml | 6 ++ .../ui-styles/src/main/res/values/bools.xml | 2 + .../BottomSheetMessagePreviewItem.kt | 15 +---- .../action/MessageActionsEpoxyController.kt | 13 +++-- .../timeline/factory/MessageItemFactory.kt | 14 +++-- .../timeline/item/MessageLocationItem.kt | 15 +---- .../im/vector/app/features/location/Config.kt | 28 +-------- .../location/LocationPreviewFragment.kt | 3 +- .../location/LocationSharingFragment.kt | 6 +- .../app/features/location/MapTilerMapView.kt | 12 +--- .../app/features/location/UrlMapProvider.kt | 58 +++++++++++++++++++ 11 files changed, 102 insertions(+), 70 deletions(-) create mode 100644 library/ui-styles/src/main/res/values-ldrtl/bools.xml create mode 100644 vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt diff --git a/library/ui-styles/src/main/res/values-ldrtl/bools.xml b/library/ui-styles/src/main/res/values-ldrtl/bools.xml new file mode 100644 index 0000000000..27b280985f --- /dev/null +++ b/library/ui-styles/src/main/res/values-ldrtl/bools.xml @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/library/ui-styles/src/main/res/values/bools.xml b/library/ui-styles/src/main/res/values/bools.xml index 93d5f925af..9966999f28 100644 --- a/library/ui-styles/src/main/res/values/bools.xml +++ b/library/ui-styles/src/main/res/values/bools.xml @@ -4,4 +4,6 @@ false + false + \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index 8805c07109..5295cbaec3 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -36,9 +36,6 @@ 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.INITIAL_MAP_ZOOM_IN_TIMELINE -import im.vector.app.features.location.LocationData -import im.vector.app.features.location.getStaticMapUrl import im.vector.app.features.media.ImageContentRenderer import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence import org.matrix.android.sdk.api.util.MatrixItem @@ -74,7 +71,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel() { @@ -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(catchError = true)?.let { - LocationData.create(it.getUri()) - } + val locationUrl = state.timelineEvent()?.root?.getClearContent() + ?.toModel(catchError = true) + ?.let { LocationData.create(it.getUri()) } + ?.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) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index b0bdef5636..8355aeed90 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -72,7 +72,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.INITIAL_MAP_ZOOM_IN_TIMELINE import im.vector.app.features.location.LocationData +import im.vector.app.features.location.UrlMapProvider import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import im.vector.app.features.settings.VectorPreferences @@ -129,7 +131,9 @@ class MessageItemFactory @Inject constructor( private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker, private val locationPinProvider: LocationPinProvider, private val vectorPreferences: VectorPreferences, - private val resources: Resources) { + private val urlMapProvider: UrlMapProvider, + private val resources: Resources +) { // TODO inject this properly? private var roomId: String = "" @@ -212,13 +216,15 @@ class MessageItemFactory @Inject constructor( val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60) val height = dimensionConverter.dpToPx(200) + val locationUrl = locationData?.let { + urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height) + } + return MessageLocationItem_() .attributes(attributes) - .locationData(locationData) + .locationUrl(locationUrl) .userId(informationData.senderId) .locationPinProvider(locationPinProvider) - .mapWidth(width) - .mapHeight(height) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(mapCallback) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index 619d799ec5..1e8ce4fb4d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -24,9 +24,6 @@ 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.location.INITIAL_MAP_ZOOM_IN_TIMELINE -import im.vector.app.features.location.LocationData -import im.vector.app.features.location.getStaticMapUrl @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageLocationItem : AbsMessageItem() { @@ -39,7 +36,7 @@ abstract class MessageLocationItem : AbsMessageItem( var callback: Callback? = null @EpoxyAttribute - var locationData: LocationData? = null + var locationUrl: String? = null @EpoxyAttribute var userId: String? = null @@ -47,17 +44,11 @@ abstract class MessageLocationItem : AbsMessageItem( @EpoxyAttribute var locationPinProvider: LocationPinProvider? = null - @EpoxyAttribute - var mapWidth: Int = 1200 - - @EpoxyAttribute - var mapHeight: Int = 800 - override fun bind(holder: Holder) { super.bind(holder) renderSendState(holder.view, null) - val location = locationData ?: return + val location = locationUrl ?: return val locationOwnerId = userId ?: return holder.view.onClick { @@ -65,7 +56,7 @@ abstract class MessageLocationItem : AbsMessageItem( } GlideApp.with(holder.staticMapImageView) - .load(getStaticMapUrl(location.latitude, location.longitude, INITIAL_MAP_ZOOM_IN_TIMELINE, mapWidth, mapHeight)) + .load(location) .apply(RequestOptions.centerCropTransform()) .into(holder.staticMapImageView) diff --git a/vector/src/main/java/im/vector/app/features/location/Config.kt b/vector/src/main/java/im/vector/app/features/location/Config.kt index eb5f1ac3b7..29ca6b81a9 100644 --- a/vector/src/main/java/im/vector/app/features/location/Config.kt +++ b/vector/src/main/java/im/vector/app/features/location/Config.kt @@ -16,34 +16,10 @@ package im.vector.app.features.location -import im.vector.app.BuildConfig - -const val MAP_STYLE_URL = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}" -private const val STATIC_MAP_IMAGE_URL = "https://api.maptiler.com/maps/basic/static/" +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 - -fun getStaticMapUrl(latitude: Double, - longitude: Double, - zoom: Double, - width: Int, - height: Int): String { - return buildString { - append(STATIC_MAP_IMAGE_URL) - append(longitude) - append(",") - append(latitude) - append(",") - append(zoom) - append("/") - append(width) - append("x") - append(height) - append(".png?key=") - append(BuildConfig.mapTilerKey) - append("&attribution=bottomleft") - } -} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt index 01417fdca0..c4f2f148bf 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt @@ -36,6 +36,7 @@ import javax.inject.Inject * TODO Move locationPinProvider to a ViewModel */ class LocationPreviewFragment @Inject constructor( + private val urlMapProvider: UrlMapProvider, private val locationPinProvider: LocationPinProvider ) : VectorBaseFragment() { @@ -53,7 +54,7 @@ class LocationPreviewFragment @Inject constructor( mapView = WeakReference(views.mapView) views.mapView.onCreate(savedInstanceState) - views.mapView.initialize() + views.mapView.initialize(urlMapProvider.mapUrl) loadPinDrawable() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 900d70e825..f6bad2826b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -35,7 +35,9 @@ import javax.inject.Inject /** * We should consider using SupportMapFragment for a out of the box lifecycle handling */ -class LocationSharingFragment @Inject constructor() : VectorBaseFragment() { +class LocationSharingFragment @Inject constructor( + private val urlMapProvider: UrlMapProvider +) : VectorBaseFragment() { private val viewModel: LocationSharingViewModel by fragmentViewModel() @@ -51,7 +53,7 @@ class LocationSharingFragment @Inject constructor() : VectorBaseFragment - map.setStyle(MAP_STYLE_URL) { style -> + map.setStyle(url) { style -> mapRefs = MapRefs( map, SymbolManager(this, map, style), diff --git a/vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt b/vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt new file mode 100644 index 0000000000..76d44f5ece --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt @@ -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") + } + } + } +} From b14e557c36deef390fa3973f34c8d3cf568ba75b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Jan 2022 07:36:19 +0100 Subject: [PATCH 53/77] Use the existing item click mechanism --- .../home/room/detail/RoomDetailAction.kt | 3 --- .../home/room/detail/RoomDetailFragment.kt | 16 ++++++++++++---- .../home/room/detail/RoomDetailViewEvents.kt | 2 -- .../home/room/detail/RoomDetailViewModel.kt | 5 ----- .../timeline/factory/MessageItemFactory.kt | 13 +------------ .../detail/timeline/item/MessageLocationItem.kt | 12 ------------ 6 files changed, 13 insertions(+), 38 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 58e36d2303..18d9994cd5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -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() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 9926ecad24..7e2bb835e0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -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,17 @@ class RoomDetailFragment @Inject constructor( } } - private fun handleShowLocationPreview(viewEvent: RoomDetailViewEvents.ShowLocation) { + private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) { + // TODO Create a helper + val geoUri = locationContent.getUri() + val locationData = LocationData.create(geoUri) navigator .openLocationSharing( context = requireContext(), roomId = roomDetailArgs.roomId, mode = LocationSharingMode.PREVIEW, - initialLocationData = viewEvent.locationData, - locationOwnerId = viewEvent.userId + initialLocationData = locationData, + locationOwnerId = senderId ) } @@ -1828,6 +1830,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") + } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index b0921e01f9..ee823adc17 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -83,6 +83,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() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 6e14b0fc76..1bb70e29d7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -385,14 +385,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 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 8355aeed90..0b0373a889 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -34,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 @@ -188,7 +187,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) } @@ -200,19 +199,10 @@ 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 mapCallback: MessageLocationItem.Callback = object : MessageLocationItem.Callback { - override fun onMapClicked() { - locationData?.let { - callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(it, informationData.senderId)) - } - } - } - val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60) val height = dimensionConverter.dpToPx(200) @@ -227,7 +217,6 @@ class MessageItemFactory @Inject constructor( .locationPinProvider(locationPinProvider) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) - .callback(mapCallback) } private fun buildPollItem(pollContent: MessagePollContent, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index 1e8ce4fb4d..6f0b6abb72 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -21,20 +21,12 @@ 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.onClick import im.vector.app.core.glide.GlideApp import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageLocationItem : AbsMessageItem() { - interface Callback { - fun onMapClicked() - } - - @EpoxyAttribute - var callback: Callback? = null - @EpoxyAttribute var locationUrl: String? = null @@ -51,10 +43,6 @@ abstract class MessageLocationItem : AbsMessageItem( val location = locationUrl ?: return val locationOwnerId = userId ?: return - holder.view.onClick { - callback?.onMapClicked() - } - GlideApp.with(holder.staticMapImageView) .load(location) .apply(RequestOptions.centerCropTransform()) From 83ed80e6d8cd4014a101ffd8091f71e355c64f64 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Jan 2022 07:40:41 +0100 Subject: [PATCH 54/77] Rename fun for clarity --- .../api/session/room/model/message/MessageLocationContent.kt | 2 +- .../app/features/home/room/detail/RoomDetailFragment.kt | 4 ++-- .../detail/timeline/action/MessageActionsEpoxyController.kt | 2 +- .../home/room/detail/timeline/factory/MessageItemFactory.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt index bf51e7177b..c090487c58 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt @@ -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 } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 7e2bb835e0..8db3bc2fa8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -614,7 +614,7 @@ class RoomDetailFragment @Inject constructor( private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) { // TODO Create a helper - val geoUri = locationContent.getUri() + val geoUri = locationContent.getBestGeoUri() val locationData = LocationData.create(geoUri) navigator .openLocationSharing( @@ -1948,7 +1948,7 @@ class RoomDetailFragment @Inject constructor( when (action.messageContent) { is MessageTextContent -> shareText(requireContext(), action.messageContent.body) is MessageLocationContent -> { - LocationData.create(action.messageContent.getUri())?.let { + LocationData.create(action.messageContent.getBestGeoUri())?.let { openLocation(requireActivity(), it.latitude, it.longitude) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 59e54f17ab..14f56a7846 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -79,7 +79,7 @@ class MessageActionsEpoxyController @Inject constructor( val bindingOptions = spanUtils.getBindingOptions(body) val locationUrl = state.timelineEvent()?.root?.getClearContent() ?.toModel(catchError = true) - ?.let { LocationData.create(it.getUri()) } + ?.let { LocationData.create(it.getBestGeoUri()) } ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } bottomSheetMessagePreviewItem { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 0b0373a889..8f88be0683 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -200,7 +200,7 @@ class MessageItemFactory @Inject constructor( informationData: MessageInformationData, highlight: Boolean, attributes: AbsMessageItem.Attributes): MessageLocationItem? { - val geoUri = locationContent.getUri() + val geoUri = locationContent.getBestGeoUri() val locationData = LocationData.create(geoUri) val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60) From 303a858423b7f77af1b9d1a21028adf5c3694ba9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Jan 2022 07:58:55 +0100 Subject: [PATCH 55/77] Create an extension, improve the parsing algorithm, add robustness and unit test it --- .../home/room/detail/RoomDetailAction.kt | 2 +- .../home/room/detail/RoomDetailFragment.kt | 9 +-- .../home/room/detail/RoomDetailViewEvents.kt | 1 - .../home/room/detail/RoomDetailViewModel.kt | 1 - .../action/MessageActionsEpoxyController.kt | 4 +- .../timeline/factory/MessageItemFactory.kt | 7 +-- .../app/features/location/LocationData.kt | 61 ++++++++++--------- .../app/features/location/LocationDataTest.kt | 60 ++++++++++++++++++ 8 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 18d9994cd5..14c8e598f8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -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() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 8db3bc2fa8..b58a1d627e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -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 @@ -613,15 +613,12 @@ class RoomDetailFragment @Inject constructor( } private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) { - // TODO Create a helper - val geoUri = locationContent.getBestGeoUri() - val locationData = LocationData.create(geoUri) navigator .openLocationSharing( context = requireContext(), roomId = roomDetailArgs.roomId, mode = LocationSharingMode.PREVIEW, - initialLocationData = locationData, + initialLocationData = locationContent.toLocationData(), locationOwnerId = senderId ) } @@ -1948,7 +1945,7 @@ class RoomDetailFragment @Inject constructor( when (action.messageContent) { is MessageTextContent -> shareText(requireContext(), action.messageContent.body) is MessageLocationContent -> { - LocationData.create(action.messageContent.getBestGeoUri())?.let { + action.messageContent.toLocationData()?.let { openLocation(requireActivity(), it.latitude, it.longitude) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index ee823adc17..86240a5ffe 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -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 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 1bb70e29d7..9149ae1dca 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -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 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 14f56a7846..086a093068 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -40,8 +40,8 @@ import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovement import im.vector.app.features.home.room.detail.timeline.tools.linkify import im.vector.app.features.html.SpanUtils import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE -import im.vector.app.features.location.LocationData 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 @@ -79,7 +79,7 @@ class MessageActionsEpoxyController @Inject constructor( val bindingOptions = spanUtils.getBindingOptions(body) val locationUrl = state.timelineEvent()?.root?.getClearContent() ?.toModel(catchError = true) - ?.let { LocationData.create(it.getBestGeoUri()) } + ?.toLocationData() ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } bottomSheetMessagePreviewItem { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 8f88be0683..352b87a4d8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -72,8 +72,8 @@ 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.INITIAL_MAP_ZOOM_IN_TIMELINE -import im.vector.app.features.location.LocationData 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 @@ -200,13 +200,10 @@ class MessageItemFactory @Inject constructor( informationData: MessageInformationData, highlight: Boolean, attributes: AbsMessageItem.Attributes): MessageLocationItem? { - val geoUri = locationContent.getBestGeoUri() - val locationData = LocationData.create(geoUri) - val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60) val height = dimensionConverter.dpToPx(200) - val locationUrl = locationData?.let { + val locationUrl = locationContent.toLocationData()?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height) } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt index c3ff09ebcd..0a88fb52d9 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt @@ -17,41 +17,44 @@ 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 + ) +} + diff --git a/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt b/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt new file mode 100644 index 0000000000..fcfff0096f --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt @@ -0,0 +1,60 @@ +/* + * 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 org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeNull +import org.junit.Test + +class LocationDataTest { + @Test + fun validCases() { + parseGeo("geo:12.34,56.78;13.56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = 13.56) + parseGeo("geo:12.34,56.78") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = null) + // Error is ignored in case of invalid uncertainty + parseGeo("geo:12.34,56.78;13.5z6") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = null) + parseGeo("geo:12.34,56.78;13. 56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = null) + // Space are ignored (trim) + parseGeo("geo: 12.34,56.78;13.56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = 13.56) + parseGeo("geo:12.34,56.78; 13.56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = 13.56) + } + + @Test + fun invalidCases() { + parseGeo("").shouldBeNull() + parseGeo("geo").shouldBeNull() + parseGeo("geo:").shouldBeNull() + parseGeo("geo:12.34").shouldBeNull() + parseGeo("geo:12.34;13.56").shouldBeNull() + parseGeo("gea:12.34,56.78;13.56").shouldBeNull() + parseGeo("geo:12.x34,56.78;13.56").shouldBeNull() + parseGeo("geo:12.34,56.7y8;13.56").shouldBeNull() + // Spaces are not ignored if inside the numbers + parseGeo("geo:12.3 4,56.78;13.56").shouldBeNull() + parseGeo("geo:12.34,56.7 8;13.56").shouldBeNull() + // Or in the protocol part + parseGeo(" geo:12.34,56.78;13.56").shouldBeNull() + parseGeo("ge o:12.34,56.78;13.56").shouldBeNull() + parseGeo("geo :12.34,56.78;13.56").shouldBeNull() + } +} From 99f82d9691a5eff0e90e4479cf766abe6fc845d6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Jan 2022 08:34:20 +0100 Subject: [PATCH 56/77] Avoid taking into account network location if we have gps location. --- .../app/features/location/LocationTracker.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index ff66baf5ca..d1f06c9d51 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -23,6 +23,7 @@ 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 @@ -39,9 +40,12 @@ class LocationTracker @Inject constructor( private var callback: Callback? = null + private var hasGpsProviderLocation = false + @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) fun start(callback: Callback?) { Timber.d("## LocationTracker. start()") + hasGpsProviderLocation = false this.callback = callback if (locationManager == null) { @@ -82,7 +86,16 @@ class LocationTracker @Inject constructor( } override fun onLocationChanged(location: Location) { - Timber.d("## LocationTracker. onLocationChanged") + if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { + Timber.d("## LocationTracker. onLocationChanged: $location") + } else { + Timber.d("## LocationTracker. onLocationChanged") + } + if (location.provider != LocationManager.GPS_PROVIDER && hasGpsProviderLocation) { + // Ignore this update + Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps location") + return + } callback?.onLocationUpdate(location.toLocationData()) } From a8c251f6f5a951c571b07d674bcedff4dee20a25 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Jan 2022 08:51:30 +0100 Subject: [PATCH 57/77] Avoid taking into account any provider location if we have gps location. --- .../app/features/location/LocationTracker.kt | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index d1f06c9d51..bf59a08119 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -56,13 +56,19 @@ class LocationTracker @Inject constructor( 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 -> - Timber.d("## LocationTracker. lastKnownLocation") - callback?.onLocationUpdate(lastKnownLocation.toLocationData()) + if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { + Timber.d("## LocationTracker. lastKnownLocation: $lastKnownLocation") + } else { + Timber.d("## LocationTracker. lastKnownLocation") + } + onLocationChanged(lastKnownLocation) } locationManager.requestLocationUpdates( @@ -91,10 +97,21 @@ class LocationTracker @Inject constructor( } else { Timber.d("## LocationTracker. onLocationChanged") } - if (location.provider != LocationManager.GPS_PROVIDER && hasGpsProviderLocation) { - // Ignore this update - Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps location") - return + notifyLocation(location) + } + + private fun notifyLocation(location: Location) { + when (location.provider) { + LocationManager.GPS_PROVIDER -> { + hasGpsProviderLocation = true + } + else -> { + if (hasGpsProviderLocation) { + // Ignore this update + Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps location") + return + } + } } callback?.onLocationUpdate(location.toLocationData()) } From 2fbb4340996ee94b71a4832051cd8f4c6cdce006 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Jan 2022 08:52:20 +0100 Subject: [PATCH 58/77] Format --- .../main/java/im/vector/app/features/location/LocationData.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt index 0a88fb52d9..a69d8d20e3 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt @@ -57,4 +57,3 @@ fun parseGeo(geo: String): LocationData? { uncertainty = uncertainty ) } - From fee155ba1e79ff607fc3ecc6d397ce68e1121cfd Mon Sep 17 00:00:00 2001 From: libexus Date: Thu, 27 Jan 2022 17:21:31 +0000 Subject: [PATCH 59/77] Translated using Weblate (German) Currently translated at 99.6% (2741 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/de/ --- vector/src/main/res/values-de/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml index 4d92def9b1..e8a961dba5 100644 --- a/vector/src/main/res/values-de/strings.xml +++ b/vector/src/main/res/values-de/strings.xml @@ -3082,4 +3082,13 @@ Wähle wo deine Gespräche liegen, für Kontrolle und Unabhängigkeit. Verbunden mit Matrix. Sichere und unabhängige Kommunikation, die für die gleiche Vertraulichkeit sorgt, wie ein Gespräch von Angesicht zu Angesicht in deinem eigenen Zuhause. Standort + Fehlerhaft konfiguriertes Vertrauenslevel + Die Verschlüsselung ist fehlerhaft konfiguriert + Bitte kontaktiere einen Admin, um die Verschlüsselung zurückzusetzen. + Die Verschlüsselung wurde fehlerhaft konfiguriert. + Die Verschlüsselung wurde fehlerhaft konfiguriert und du kannst keine Nachrichten senden. Klicke, um die Einstellungen zu öffnen. + Die Verschlüsselung wurde fehlerhaft konfiguriert und du kannst keine Nachrichten senden. Bitte kontaktiere einen Admin, um die Verschlüsselung wiederherzustellen. + Geteilte Standorte anzeigen + Sobald aktiviert, kannst du deinen Standort in jeden Raum senden + Hat den Standort geteilt \ No newline at end of file From 45c0761b740853e7034e0f242f9b1e39dcdbcd05 Mon Sep 17 00:00:00 2001 From: Paragoumba Date: Thu, 27 Jan 2022 17:42:58 +0000 Subject: [PATCH 60/77] Translated using Weblate (French) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/ --- vector/src/main/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml index 3ea79e0a9d..08dcb09190 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -3078,7 +3078,7 @@ Partager la localisation Localisation Partager la localisation - Les résultats ne sont dévoilés que quand vous terminez le sondage + Les résultats ne sont dévoilés que lorsque vous terminez le sondage Sondage fermé Les votants voient les résultats dès qu\'ils ont votés Ouvrir le sondage From c3b4e4f2f94a497d81884b416f37c3f9ce86e9d8 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 28 Jan 2022 18:15:41 +0000 Subject: [PATCH 61/77] Translated using Weblate (Hungarian) Currently translated at 100.0% (2752 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/hu/ --- vector/src/main/res/values-hu/strings.xml | 43 ++++++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml index ccdbb41c35..0a6b6d9a97 100644 --- a/vector/src/main/res/values-hu/strings.xml +++ b/vector/src/main/res/values-hu/strings.xml @@ -777,7 +777,7 @@ Figyelmeztetés: ez a fájl törlésre kerülhet, ha az alkalmazást törli.Biztos, hogy hanghívást akarsz indítani\? Biztos, hogy videóhívást akarsz indítani\? Csoportok listája - A felhasználó kitiltása kirúgja őt a szobából, és megakadályozza, hogy újra csatlakozhasson. + A felhasználó kitiltása eltávolítja őt a szobából, és megakadályozza, hogy újra csatlakozhasson. Összes üzenet (hangos) Összes üzenet Csak megemlítések @@ -1009,7 +1009,7 @@ Matrixban az üzenetek láthatósága hasonlít az e-mailre. Az üzenet törlés Olvasás visszajelzés megjelenítése További információér kattints az olvasás visszaigazolásokra. Be-, és kilépési események megjelenítése - Meghívások, kirúgások és kitiltások nem változtak. + Meghívások, eltávolítások és kitiltások nem változtak. Fiók események megjelenítése Avatar és név változásokat tartalmaz. Jelszó @@ -2751,7 +2751,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró 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. Az üzenet szerkesztőhöz emodzsi billentyűzet gomb hozzáadása Emodzsi billentyűzet megjelenítése - 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. + 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. Használd a /confetti parancsot vagy küldj üzenetet ami ❄️-t vagy 🎉-t tartalmaz Beszélgetés effektek megjelenítése Szoba tagok státusz eseményeinek megjelenítése @@ -3015,7 +3015,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Eredmény %1$d szavazat alapján - Nem szavazott senki + %1$d leadott szavazat. Szavazz az eredmények megtekintéséhez %1$d leadott szavazat. Szavazz az eredmények megtekintéséhez @@ -3061,6 +3061,39 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Becenév színének megváltoztatása Már van fiókom Rázd fel a csoportjaidat. - Lépj kapcsolatba bárkivel. + Biztonságos üzenetküldés. Te irányítasz. + Tartózkodási hely megosztása + A felhasználó földrajzi helyzetének megjelenítése az idővonalon + A beállítás után bármelyik szobában megoszthatod a földrajzi helyzetedet + Földrajzi hely megosztás engedélyezése + Megnyitás ezzel + ${app_name} nem fér hozzá a helyadatodhoz. Próbáld újra később. + ${app_name} nem fér hozzá a földrajzi helyzetedhez + Tartózkodási hely megosztása + Tartózkodási hely megosztása + Földrajzi helyzet + Tartózkodási hely megosztása + Az eredmény csak a szavazás végeztével válik láthatóvá + Lezárt szavazás + A szavazók a szavazásuk után látják a szavazatokat + Szavazás megnyitása + Szavazás típusa + SZAVAZÁS SZERKESZTÉSE + Szavazás szerkesztése + Nem adtak le szavazatot + Megbízhatósági szint beállítási hiba + A titkosítási beállítás hibás + Titkosítás helyreállítása + Kérjük vedd fel a kapcsolatot az adminisztrátorral a titkosítás helyreállítása érdekében. + A titkosítás beállítása hibás. + A földrajzi helyzetüket megosztották + Fiók létrehozása + Üzenetküldés a csoportodnak. + Telefonszám nélkül végpontok között titkosított. Reklámok és adatbányászat nélkül. + Válaszd meg hol legyenek a beszélgetéseid tárolva, visszaadja az irányítást és függetlenné tesz. Csatlakozva a Matrixhoz. + Biztonságos és független kommunikáció ami olyan biztonságos mintha valakivel négyszemközt beszélgetnél a házadban. + Földrajzi helyzet + A titkosítás beállítása hibás így nem lehet üzenetet küldeni. Kattints a beállításokért. + 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. \ No newline at end of file From ed21744402233394fcc72f351f3a8fd3359e4045 Mon Sep 17 00:00:00 2001 From: Hasan Date: Fri, 28 Jan 2022 11:25:28 +0000 Subject: [PATCH 62/77] Translated using Weblate (Turkish) Currently translated at 64.0% (1763 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/tr/ --- vector/src/main/res/values-tr/strings.xml | 257 +++++++++++++++++++--- 1 file changed, 232 insertions(+), 25 deletions(-) diff --git a/vector/src/main/res/values-tr/strings.xml b/vector/src/main/res/values-tr/strings.xml index 50177afcc5..a25cb317f4 100644 --- a/vector/src/main/res/values-tr/strings.xml +++ b/vector/src/main/res/values-tr/strings.xml @@ -37,7 +37,7 @@ Deşifre Edilmiş Kaynağı İncele Sil Yeniden Adlandır - İçeriği bildir + Rapor İçeriği Şu anki görüşme Devam eden konferans görüşmesi. \n%1$s veya %2$s olarak katıl @@ -118,7 +118,7 @@ Hesap oluştur Giriş yap Çıkış yap - Ev Sunucusu URL\'si + Ana Sunucu URL\'si Ara Yeni Sohbete Başla Sesli Görüşme Başlat @@ -188,7 +188,7 @@ Hata Sistem Uyarıları Gönder - Kimlik Sunucusu URL\'si + Kimlik sunucusu URL\'si E-posta adresi E-posta adresi (isteğe bağlı) Telefon numarası @@ -213,10 +213,10 @@ Eposta ve telefon numarası ile aynı anda kayıt yaptırmak şimdilik API yüzüden desteklenmiyor. Sadece telefon numarası hesaba eklenecektir. \n \nEpostanı daha sonra profil ayarlarından ekleyebilirsin. - Ana Makine senin robot olmadığından emin olmak istiyor + Bu ana sunucu bir robot olmadığından emin olmak istiyor Kullandığın kullanıcı adı - Ana Makine: - Tanımlayıcı Sunucu: + Ana sunucu: + Kimlik sunucusu: Eposta adresimi doğruladım Şifrenizi sıfırlamak için, hesabınıza bağlı eposta adresini girin: Hesaba bağlı eposta adresi mutlaka girilmeli. @@ -248,8 +248,8 @@ Makbuz Listesini Oku Grup Listesi - 1 üyelik değişimi - %d üyelik değişimi + %d üyelik değişikliği + %d üyelik değişiklikleri Farklı gönder Orijinal @@ -325,28 +325,28 @@ Yeni Sohbet Üye ekle - 1 aktif üye - %d aktif üye + %d aktif üye + %d aktif üyeler - 1 üye - %d üye + %d üye + %d üyeler 1 üye - 1sn - %dsn + %ds 1 + %ds - 1dk + %d1dk %ddk - 1sa + %d1sa %dsa - 1g + %d1g %dg Odadan ayrıl @@ -376,7 +376,7 @@ Oturum Listesini Göster Bu işlemin geri dönüşü yok kullanıcıyı sen ile aynı erişim seviyesine getiriyorsun. \nEmin misin\? - Kullanıcıyı bu engellersen bu odadan kovulacak ve bir daha giremeyecek. + Kullanıcıyı yasaklamak onları bu odadan çıkaracak ve tekrar katılmalarını engelleyecektir. Neden %s adlı kullanıcılar(ı) bu sohbete davet etmek istediğinden emin misin\? "%1$s, " @@ -405,10 +405,10 @@ Gönderilmeyen mesajları tekrar gönder Gönderilmeyen mesajları sil Dosya bulunamadı - Bu odada mesajlaşmaya yetkin yok + Bu odaya mesaj gönderme izniniz yok. - 1 yeni mesaj - %d yeni mesaj + %d 1 yeni mesaj + %d yeni mesajlar Güven Güvenme @@ -426,7 +426,7 @@ Dosyalar Ayarlar - 1 tane seçili + %d1 tane seçili %d tane seçili Hatalı ID. Eposta adresi ya da Matrix ID\'si olmalıdır \'@localpart:domain\' @@ -447,8 +447,8 @@ DOSYALAR Dizinlere gözat - 1 oda - %d oda + %d1 oda + %d odalar %2$s adında %1$s oda bulundu @@ -1068,7 +1068,7 @@ Hesap kurtarma epostası kaydedin. Daha sonra isteğe bağlı eposta veya telefon nuraması kullanarak sizi tanıyan kişilerin sizi bulabilmesini sağlayın. Cihazın saldırılara açık, eski bir TLS güvenlik protokolü kullanıyor; güvenliğin için bağlantın engellendi İkincil çağrı yardımcı sunucusuna izin ver - Anasunucunuz çağrı yardımcı sunucusu vermez ise %s çağrı yardımcı sunucusu olarak kullanılacaktır (Çağrıda IP adresiniz paylaşılacaktır) + Ana sunucunuz bir tane sunmadığında %sas assist kullanacaktır (arama sırasında IP adresiniz paylaşılacaktır) Bu eylemi gerçekleştirebilmek için ayarlarınızdan bir kimlik sunucusu ekleyin. Şifreni doğrula ${app_name}, cihazın sınırlı kaynaklarını (pil) koruyacak şekilde arka planda senkronize olur. @@ -1819,4 +1819,211 @@ Burada yükselttiniz. tüm oda üyeleri, katıldıkları andan itibaren. tüm oda üyeleri, davet edildikleri andan itibaren. + Hiçbiri + Yalnızca Söz ve Anahtar Kelimeler + Alanı yükseltin + m.room.server_acl olaylarını gönder + Alan adını değiştir + Alan şifrelemeyi etkinleştir + Alanın ana adresini değiştirme + Alan avatarını değiştir + Bu alanın çeşitli bölümlerini değiştirmek için gereken rolleri güncelleme izniniz yok + Bu alanın çeşitli bölümlerini değiştirmek için gereken rolleri seçin + Alanın çeşitli bölümlerini değiştirmek için gereken rolleri görüntüleyin ve güncelleyin. + Alan izinleri + Şifreleme yanlış yapılandırıldı, böylece mesaj gönderemezsiniz. Ayarlar\'ı açmak için tıklatın. + Şifreleme yanlış yapılandırıldı, böylece mesaj gönderemezsiniz. Şifrelemeyi geçerli bir duruma geri yüklemek için lütfen bir yöneticiyle iletişime geçin. + Yasağı kaldırmak, kullanıcının alana tekrar katılmasına izin verir. + Kullanıcıyı yasaklamak onları bu alandan kaldıracak ve tekrar katılmalarını engelleyecektir. + Kullanıcı bu alandan kaldırılacaktır. +\n +\nTekrar katılmalarını önlemek için, onları yasaklamalısınız. + Yine de Devam Et + Arama sonlandırılıyor… + Cevap yok + Aradığınız kullanıcı meşgul. + Kullanıcı meşgul + %s ile sesli arama + %s ile görüntülü arama + + Cevapsız görüntülü arama + %d cevapsız görüntülü aramalar + + + Cevapsız sesli arama + %d cevapsız sesli aramalar + + Çağrı çalıyor… + Yetkisiz, eksik geçerli kimlik doğrulama kimlik bilgileri + Ana sunucuyu seçin + %s url\'sinde bir ana sunucuya ulaşılamıyor. Lütfen bağlantınızı kontrol edin veya bir ana sunucuyu manuel olarak seçin. + Varsayılan olarak kullan ve tekrar sorma + Her zaman sor + Ana sunucu API URL\'si + Alanlar + Davetliler + Açık içeriğe sahip odalar da dahil olmak üzere oda dizinindeki tüm odaları göster. + Açık içerikli odaları göster + Oda rehberi + Önerilen Odalar + Yeni değer + Şimdi değil + Etkinleştirmek + Geri dönüş + Değiştirmek + Eksik izinler + Sesli mesaj göndermek için lütfen Mikrofona izin verin. + Bu işlemi gerçekleştirmek için lütfen Kameraya sistem ayarlarından izin verin. + Bazı izinler eksik, bu eylemi gerçekleştirmek için lütfen sistem ayarlarından izinleri verin. + Alanlar + daha fazla bilgi edin + Bildirimleri dinleme + Uçtan uca şifrelemeyi etkinleştirdiniz (tanınmayan algoritma %1$s). + %1$s uçtan uca şifrelemeyi etkinleştirdi (tanınmayan algoritma %2$s). + Uçtan uca şifrelemeyi açtın. + %1$s uçtan uca şifrelemeyi etkinleştirdi. + Konukların odaya katılmasını engellediniz. + %1$s konukların odaya katılmasını engelledi. + Konukların odaya katılmasını engellediniz. + %1$s konukların odaya katılmasını engelledi. + Konukların buraya katılmasına izin verdiniz. + %1$s konukların buraya katılmasına izin verdi. + Konukların odaya katılmasına izin verdiniz. + %1$s konukların odaya katılmasına izin verdi. + Bu odanın adreslerini değiştirdin. + %1$s bu odanın adreslerini değiştirdi. + Bu odanın ana ve alternatif adreslerini değiştirdiniz. + %1$s bu odanın ana ve alternatif adreslerini değiştirdi. + Bu odanın alternatif adreslerini değiştirdiniz. + %1$s bu odanın alternatif adreslerini değiştirdi. + + Bu oda için %1$s alternatif adresini kaldırdınız. + Bu oda için %1$s alternatif adreslerini kaldırdınız. + + + %1$s bu oda için %2$s alternatif adresini kaldırdı. + %1$s bu oda için %2$s alternatif adreslerini kaldırdı. + + + Bu oda için %1$s alternatif adresini eklediniz. + Bu oda için %1$s alternatif adreslerini eklediniz. + + + %1$s, bu oda için %2$s alternatif adresini ekledi. + %1$s, bu oda için %2$s alternatif adreslerini ekledi. + + Bu odanın ana adresini kaldırdınız. + %1$s bu odanın ana adresini kaldırdı. + Bu odanın ana adresini %1$s olarak ayarladınız. + %1$s, bu odanın ana adresini %2$s olarak belirledi. + Bu oda için adres olarak %1$s eklediniz ve %2$s\'i kaldırdınız. + %1$s, bu oda için adres olarak %2$s ekledi ve %3$s\'i kaldırdı. + + %1$s adresini bu odanın adresi olarak kaldırdınız. + %1$s adresini bu oda için adres olarak kaldırdınız. + + + %1$s, %2$s adresini bu odanın adresi olarak kaldırdı. + %1$s, %2$s adresini bu odanın adresi olarak kaldırdı. + + + Bu oda için adres olarak %1$s eklediniz. + Bu oda için adres olarak %1$s eklediniz. + + + %1$s, bu oda için bir adres olarak %2$s ekledi. + %1$s, bu oda için adres olarak %2$s ekledi. + + %1$s adlı kişinin davetini geri çektiniz. Neden: %2$s + %1$s, %2$s adlı kişinin davetini geri çekti. Neden: %3$s + %1$s davetini kabul ettiniz. Neden: %2$s + %1$s, %2$s davetini kabul etti. Neden: %3$s + %1$s için odaya katılma davetini iptal ettiniz. Neden: %2$s + %1$s, %2$s için odaya katılma davetini iptal etti. Neden: %3$s + Odaya katılması için %1$s\'a bir davetiye gönderdiniz. Neden: %2$s + %1$s, odaya katılması için %2$s\'a bir davetiye gönderdi. Neden: %3$s + %1$s\'i yasakladınız. Neden: %2$s + %1$s, %2$s\'yi yasakladı. Neden: %3$s + %1$s yasağını kaldırdınız. Neden: %2$s + %1$s planlanmamış %2$s. Nedeni: %3$s + %1$s kaldırdınız. Nedeni: %2$s + %1$s %2$s kaldırıldı. Nedeni: %3$s + Daveti reddettiniz. Sebep: %1$s + %1$s daveti reddetti. Neden: %2$s + Gittin. Sebep: %1$s + %1$s kaldı. Neden: %2$s + Odadan ayrıldın. Sebep: %1$s + %1$s odadan ayrıldı. Neden: %2$s + Katıldın. Sebep: %1$s + %1$s katıldı. Neden: %2$s + Odaya katıldın. Sebep: %1$s + %1$s odaya katıldı. Neden: %2$s + %1$s sizi davet etti. Neden: %2$s + %1$s davet ettiniz. Neden: %2$s + %1$s, %2$s\'i davet etti. Neden: %3$s + Senin davetiyen. Sebep: %1$s + %1$s\'in davetiyesi. Neden: %2$s + Gönderme kuyruğunu temizle + Mesajı gönderildi + İlk senkronizasyon: +\nHesap verilerini içe aktarma + İlk senkronizasyon: +\nToplulukları içe aktarma + İlk senkronizasyon: +\nSol odalar içe aktarılıyor + İlk senkronizasyon: +\nDavetli odaları içe aktarma + İlk senkronizasyon: +\nKonuşmalarınız yükleniyor +\nÇok sayıda odaya katıldıysanız, bu biraz zaman alabilir + İlk senkronizasyon: +\nOdaları içe aktarma + İlk senkronizasyon: +\nKripto içe aktarma + İlk senkronizasyon: +\nHesap içe aktarılıyor… + İlk senkronizasyon: +\nVeriler indiriliyor… + İlk senkronizasyon: +\nSunucu yanıtı bekleniyor… + Boş oda (%s idi) + + %1$s ve 1 kişi daha + %1$s ve %2$d kişi daha + + + %1$s, %2$s, %3$s ve %4$d diğer + %1$s, %2$s, %3$s ve %4$d kişi daha + + %1$s, %2$s, %3$s ve %4$s + %1$s ve %2$s + %s\'den davet + Bu odaya katılmanıza izin verilmiyor + Şu anda boş bir odaya yeniden katılmak mümkün değildir. + redaksiyon yapılamadı + Gönderenin cihazı bize bu mesajın anahtarlarını göndermedi. + ** Şifre çözülemiyor: %s ** + %1$s, %2$s ile %3$s arasında + %1$s, %2$s\'nin güç seviyesini değiştirdi. + %1$s\'nin güç seviyesini değiştirdiniz. + Özel (%1$d) + Moderatör + Admin + %1$s tarafından video konferans başlatıldı + %1$s widget\'ını değiştirdiniz + %1$s, %2$s widget\'ı ekledi + %1$s, %2$s profilini güncelledi + VoIP konferansı bitti + VoIP konferansı başladı + Bir VoIP konferansı talep ettiniz + %1$s bir VoIP konferansı istedi + 🎉 Tüm sunucuların katılımı yasaklanmıştır! Bu oda artık kullanılamaz. + • IP değişmezleriyle eşleşen sunucular artık yasaklanmıştır. + • IP değişmez değerleriyle eşleşen sunuculara artık izin verilmektedir. + • %s ile eşleşen sunucular izin verilenler listesinden kaldırıldı. + • %s ile eşleşen sunuculara artık izin veriliyor. + • %s ile eşleşen sunucular yasak listesinden kaldırıldı. + • %s ile eşleşen sunucular artık yasaklandı. + Bu oda için sunucu ACL\'lerini değiştirdiniz. + %s, bu oda için sunucu ACL\'lerini değiştirdi. \ No newline at end of file From 47c940451806d43202e2482d759eccd5d792eada Mon Sep 17 00:00:00 2001 From: libexus Date: Thu, 27 Jan 2022 17:16:38 +0000 Subject: [PATCH 63/77] Translated using Weblate (German) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/de/ --- fastlane/metadata/android/de-DE/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/de-DE/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/de-DE/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/de-DE/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/de-DE/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/de-DE/changelogs/40103150.txt diff --git a/fastlane/metadata/android/de-DE/changelogs/40103130.txt b/fastlane/metadata/android/de-DE/changelogs/40103130.txt new file mode 100644 index 0000000000..82aaadd5f3 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103140.txt b/fastlane/metadata/android/de-DE/changelogs/40103140.txt new file mode 100644 index 0000000000..6032784f64 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103150.txt b/fastlane/metadata/android/de-DE/changelogs/40103150.txt new file mode 100644 index 0000000000..6e324d4ef5 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103150.txt @@ -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 From d9cee9f2f1efa4c5c7882d717e58eb6f2c3839e9 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 28 Jan 2022 09:11:54 +0000 Subject: [PATCH 64/77] Translated using Weblate (Hungarian) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/hu/ --- fastlane/metadata/android/hu-HU/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/hu-HU/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/hu-HU/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/hu-HU/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/hu-HU/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/hu-HU/changelogs/40103150.txt diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103130.txt b/fastlane/metadata/android/hu-HU/changelogs/40103130.txt new file mode 100644 index 0000000000..4c39f156f9 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103140.txt b/fastlane/metadata/android/hu-HU/changelogs/40103140.txt new file mode 100644 index 0000000000..b53f857896 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103150.txt b/fastlane/metadata/android/hu-HU/changelogs/40103150.txt new file mode 100644 index 0000000000..abb4bf336a --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103150.txt @@ -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 From 84373a3f4479983195b1d33859820458f8aaf574 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Thu, 27 Jan 2022 19:24:32 +0000 Subject: [PATCH 65/77] Translated using Weblate (Slovak) Currently translated at 100.0% (46 of 46 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sk/ --- fastlane/metadata/android/sk/changelogs/40103130.txt | 2 ++ fastlane/metadata/android/sk/changelogs/40103140.txt | 2 ++ fastlane/metadata/android/sk/changelogs/40103150.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/sk/changelogs/40103130.txt create mode 100644 fastlane/metadata/android/sk/changelogs/40103140.txt create mode 100644 fastlane/metadata/android/sk/changelogs/40103150.txt diff --git a/fastlane/metadata/android/sk/changelogs/40103130.txt b/fastlane/metadata/android/sk/changelogs/40103130.txt new file mode 100644 index 0000000000..31f6cf31db --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40103130.txt @@ -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 diff --git a/fastlane/metadata/android/sk/changelogs/40103140.txt b/fastlane/metadata/android/sk/changelogs/40103140.txt new file mode 100644 index 0000000000..8e21829163 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40103140.txt @@ -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 diff --git a/fastlane/metadata/android/sk/changelogs/40103150.txt b/fastlane/metadata/android/sk/changelogs/40103150.txt new file mode 100644 index 0000000000..7016270d76 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40103150.txt @@ -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 From b2bc92eebb8f52c7a6f9304dc871d7afaed189fb Mon Sep 17 00:00:00 2001 From: bmarty Date: Mon, 31 Jan 2022 00:02:22 +0000 Subject: [PATCH 66/77] Sync analytics plan --- .../app/features/analytics/plan/PerformanceTimer.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt b/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt index 2cfc366cd3..59dd997f92 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt @@ -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, From 6564146e919c4f4a58b1ea4f1c2f6d5ea78d2869 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Sun, 30 Jan 2022 15:20:00 +0000 Subject: [PATCH 67/77] Translated using Weblate (Slovak) Currently translated at 72.6% (1999 of 2752 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/ --- vector/src/main/res/values-sk/strings.xml | 104 +++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-sk/strings.xml b/vector/src/main/res/values-sk/strings.xml index e4557d5f22..5377dfe99f 100644 --- a/vector/src/main/res/values-sk/strings.xml +++ b/vector/src/main/res/values-sk/strings.xml @@ -795,7 +795,7 @@ Ste si istý, že chcete začať hlasový hovor? Ste si istí, že chcete začať video hovor? Zoznam komunít - Zakázanie používateľa ho vylúči z tejto miestnosti a zabráni mu v ďalšom vstupe. + Zakázanie používateľa ho odstráni z tejto miestnosti a zabráni mu v ďalšom vstupe. Všetky správy (hlasné) Všetky správy Len zmienky @@ -2176,4 +2176,106 @@ • Servery s číslicami ako IP adresy sú povolené. • Servery, ktorých názov sa zhoduje s %s sú povolené. • Servery, ktorých názov sa zhoduje s %s sú zakázané. + Nastaviť rolu + Fráza pre obnovenie + Editor správ + Nedá sa skenovať + Iné relácie + Aktuálna relácia + Vyčistiť údaje + Vyčistiť údaje + Knižnice tretích strán + Pomoc a podpora + Pozvať pomocou používateľského mena alebo e-mailu + Povoliť matematiku LaTeX + Reštartujte aplikáciu, aby sa zmena prejavila. + Vy máte všetko pod kontrolou. + Bezpečné zasielanie správ. + Automatické hlásenie chýb dešifrovania. + Váš systém bude automaticky odosielať záznamy, keď sa vyskytne chyba dešifrovania + Otvoriť kameru + Posielajte obrázky a videá + Odoslať nálepku + Otvoriť kontakty + Vytvoriť anketu + 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. + Vyberte si, kde budú vaše rozhovory uložené, a získajte tak kontrolu a nezávislosť. Pripojené cez Matrix. + End-to-end šifrovanie a bez potreby telefónneho čísla. Žiadne reklamy ani zneužívanie údajov. + Zasielanie správ pre váš tím. + Vytvoriť účet + Šifrovanie je nesprávne nastavené. + Obráťte sa na správcu, aby obnovil platný stav šifrovania. + Obnoviť šifrovanie + Šifrovanie je nesprávne nastavené + Žiadne odovzdané hlasy + Upraviť anketu + Výsledky sa zobrazia až po ukončení ankety + Poloha + Zdieľali svoju polohu + Zdieľať polohu + Poloha + Zdieľať polohu + Zdieľať polohu + ${app_name} nemohol získať prístup k vašej polohe + ${app_name} nemohol získať prístup k vašej polohe. Skúste to prosím neskôr. + Otvoriť s + Povoliť zdieľanie polohy + Po zapnutí tejto funkcie budete môcť odoslať svoju polohu do ľubovoľnej miestnosti + Vykresliť umiestnenie používateľa na časovej osi + Zdieľať polohu + Prémiový hosting pre organizácie + Prihlásiť sa do %1$s + Pripojiť sa k vlastnému serveru + Pripojiť sa k službám Element Matrix + Pripojiť sa k %1$s + Prémiový hosting pre organizácie + Pripojte sa k miliónom ľudí zadarmo na najväčšom verejnom serveri + Rovnako ako e-mail, aj účty majú jeden domov, hoci môžete hovoriť s kýmkoľvek + Začnite + Rozšírte a prispôsobte si možnosti + Udržujte konverzácie súkromné pomocou šifrovania + Komunikujte s ľuďmi priamo alebo v skupinách + Je to vaša konverzácia. Vlastnite ju. + Vytvorili ste miestnosť len pre pozvaných. + %1$s vytvoril miestnosť len na pozvanie. + Miestnosť ste zverejnili pre každého, kto pozná odkaz. + %1$s zverejnil miestnosť pre každého, kto pozná odkaz. + Dlhým kliknutím na miestnosť zobrazíte ďalšie možnosti + Nevykonali ste žiadne zmeny + %1$s nevykonal žiadne zmeny + 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. + Nahlásené ako spam + 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. + NAHLÁSIŤ + Dôvod nahlásenia tohto obsahu + V tejto miestnosti nie sú žiadne súbory + MÉDIÁ + V tejto miestnosti nie sú žiadne médiá + Pri načítavaní prílohy došlo k chybe. + Súbor \'%1$s\' (%2$s) je príliš veľký na odoslanie. Limit je %3$s. + Prejsť na spodok + Vytvoriť novú miestnosť + Vytvoriť novú priamu konverzáciu + Po prijatí podmienok domovského servera to skúste znova. + Zobraziť a aktualizovať role potrebné na zmenu rôznych častí priestoru. + Zobraziť a aktualizovať role potrebné na zmenu rôznych častí miestnosti. + Šifrovanie bolo nesprávne nastavené, takže nemôžete odosielať správy. Kliknutím otvorte nastavenia. + Šifrovanie bolo nesprávne nastavené, takže nemôžete odosielať správy. Obráťte sa na správcu, aby obnovil platný stav šifrovania. + Zrušenie zákazu používateľovi mu umožní znovu sa pripojiť do priestoru. + Zakázanie používateľa ho odstráni z tohto priestoru a zabráni mu v ďalšom vstupe. + Používateľ bude z tohto priestoru odstránený. +\n +\nAby ste mu zabránili v opätovnom pripojení, mali by ste ho zakázať. + Táto miestnosť nie je verejná. Bez pozvánky sa do nej nebudete môcť opätovne pripojiť. + Pokračovať aj tak + Povoľte prístup ku kontaktom. + Ak chcete skenovať QR kód, musíte povoliť prístup k fotoaparátu. + Volaný používateľ je obsadený. + Používateľ je obsadený + %s podržal hovor + Hlasový hovor s %s \ No newline at end of file From 14c4233cdcfdd082eb9e465725fc14bdfa0c32a9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 11:01:29 +0100 Subject: [PATCH 68/77] Fix some typos --- vector/src/main/res/values-tr/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vector/src/main/res/values-tr/strings.xml b/vector/src/main/res/values-tr/strings.xml index a25cb317f4..50af4a203c 100644 --- a/vector/src/main/res/values-tr/strings.xml +++ b/vector/src/main/res/values-tr/strings.xml @@ -338,15 +338,15 @@ %ds - %d1dk + %ddk %ddk - %d1sa + %dsa %dsa - %d1g + %dg %dg Odadan ayrıl @@ -407,7 +407,7 @@ Dosya bulunamadı Bu odaya mesaj gönderme izniniz yok. - %d 1 yeni mesaj + %d yeni mesaj %d yeni mesajlar Güven @@ -426,7 +426,7 @@ Dosyalar Ayarlar - %d1 tane seçili + %d tane seçili %d tane seçili Hatalı ID. Eposta adresi ya da Matrix ID\'si olmalıdır \'@localpart:domain\' @@ -447,7 +447,7 @@ DOSYALAR Dizinlere gözat - %d1 oda + %d oda %d odalar From ecd41d38260507eed54f370dcd429017cc1d31a3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 12:47:17 +0100 Subject: [PATCH 69/77] network "not live" lcoation can be more accurate than GPS "not live" location. So do not ignore them. Not sure how if this is a universal rule... --- .../app/features/location/LocationTracker.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index bf59a08119..162fbc5959 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -40,12 +40,12 @@ class LocationTracker @Inject constructor( private var callback: Callback? = null - private var hasGpsProviderLocation = false + private var hasGpsProviderLiveLocation = false @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) fun start(callback: Callback?) { Timber.d("## LocationTracker. start()") - hasGpsProviderLocation = false + hasGpsProviderLiveLocation = false this.callback = callback if (locationManager == null) { @@ -66,9 +66,9 @@ class LocationTracker @Inject constructor( if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { Timber.d("## LocationTracker. lastKnownLocation: $lastKnownLocation") } else { - Timber.d("## LocationTracker. lastKnownLocation") + Timber.d("## LocationTracker. lastKnownLocation: ${lastKnownLocation.provider}") } - onLocationChanged(lastKnownLocation) + notifyLocation(lastKnownLocation, isLive = false) } locationManager.requestLocationUpdates( @@ -95,20 +95,20 @@ class LocationTracker @Inject constructor( if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { Timber.d("## LocationTracker. onLocationChanged: $location") } else { - Timber.d("## LocationTracker. onLocationChanged") + Timber.d("## LocationTracker. onLocationChanged: ${location.provider}") } - notifyLocation(location) + notifyLocation(location, isLive = true) } - private fun notifyLocation(location: Location) { + private fun notifyLocation(location: Location, isLive: Boolean) { when (location.provider) { LocationManager.GPS_PROVIDER -> { - hasGpsProviderLocation = true + hasGpsProviderLiveLocation = isLive } else -> { - if (hasGpsProviderLocation) { + if (hasGpsProviderLiveLocation) { // Ignore this update - Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps location") + Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps live location") return } } From 4e3c730940c3de05e49bbfde8276dd56ea082c1d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 12:49:08 +0100 Subject: [PATCH 70/77] Changelog --- changelog.d/5084.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5084.bugfix diff --git a/changelog.d/5084.bugfix b/changelog.d/5084.bugfix new file mode 100644 index 0000000000..95a3a20cd1 --- /dev/null +++ b/changelog.d/5084.bugfix @@ -0,0 +1 @@ +Display static map images in the timeline and improve Location sharing feature \ No newline at end of file From 9119cf059f943ba7d85abd0c52c6d751c8e1a370 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 31 Jan 2022 12:06:21 +0000 Subject: [PATCH 71/77] adding extra logging around the push rules to help determine if we're incorrectly filter out valid notifications --- .../session/notification/ProcessEventForPushTask.kt | 1 + .../notifications/NotifiableEventProcessor.kt | 11 ++++++++--- .../features/notifications/PushRuleTriggerListener.kt | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt index da15e158e5..8b05d2ea62 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt @@ -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() diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt index 3d10d74fe3..351f085b7e 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt @@ -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> @@ -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 diff --git a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt index ff817520db..cd08820fc1 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt @@ -55,12 +55,12 @@ class PushRuleTriggerListener @Inject constructor( private suspend fun createNotifiableEvents(pushEvents: PushEvents, session: Session): List { 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 } } From 2efa67e587aafac380161c2132def05c2548feac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 14:48:54 +0100 Subject: [PATCH 72/77] Downgrade the version to 1.3.17 to prepare a corrective release. --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index a4102f7441..7b33bdd9d6 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.3.18\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.17\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" diff --git a/vector/build.gradle b/vector/build.gradle index 8792f526d4..1d880736f8 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -18,7 +18,7 @@ ext.versionMinor = 3 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 18 +ext.versionPatch = 17 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 7d463cc9bc36d194bca490735e67d66411cc4a4d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 14:51:09 +0100 Subject: [PATCH 73/77] Towncrier --- CHANGES.md | 12 ++++++++++++ changelog.d/5062.bugfix | 1 - changelog.d/5067.bugfix | 1 - changelog.d/5081.bugfix | 1 - changelog.d/5084.bugfix | 1 - changelog.d/5091.bugfix | 1 - 6 files changed, 12 insertions(+), 5 deletions(-) delete mode 100644 changelog.d/5062.bugfix delete mode 100644 changelog.d/5067.bugfix delete mode 100644 changelog.d/5081.bugfix delete mode 100644 changelog.d/5084.bugfix delete mode 100644 changelog.d/5091.bugfix diff --git a/CHANGES.md b/CHANGES.md index 4d2394db1f..b1b0deee2c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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) ====================================== diff --git a/changelog.d/5062.bugfix b/changelog.d/5062.bugfix deleted file mode 100644 index ec24bfd6c1..0000000000 --- a/changelog.d/5062.bugfix +++ /dev/null @@ -1 +0,0 @@ -Show the legal mention of mapbox when sharing location \ No newline at end of file diff --git a/changelog.d/5067.bugfix b/changelog.d/5067.bugfix deleted file mode 100644 index 7ad88b608d..0000000000 --- a/changelog.d/5067.bugfix +++ /dev/null @@ -1 +0,0 @@ -Poll cannot end in some unencrypted rooms \ No newline at end of file diff --git a/changelog.d/5081.bugfix b/changelog.d/5081.bugfix deleted file mode 100644 index 6ec6dcc43e..0000000000 --- a/changelog.d/5081.bugfix +++ /dev/null @@ -1 +0,0 @@ -Selecting Transfer in a call should immediately put the other person on hold until the call connects or the Transfer is cancelled. \ No newline at end of file diff --git a/changelog.d/5084.bugfix b/changelog.d/5084.bugfix deleted file mode 100644 index 95a3a20cd1..0000000000 --- a/changelog.d/5084.bugfix +++ /dev/null @@ -1 +0,0 @@ -Display static map images in the timeline and improve Location sharing feature \ No newline at end of file diff --git a/changelog.d/5091.bugfix b/changelog.d/5091.bugfix deleted file mode 100644 index cf55a6dd10..0000000000 --- a/changelog.d/5091.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixing crashes when quickly scrolling or restoring the room timeline \ No newline at end of file From c296b076cc748d23dc5abeff5f16d91db1eb2b60 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 14:51:49 +0100 Subject: [PATCH 74/77] fastlane --- fastlane/metadata/android/en-US/changelogs/40103170.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/40103170.txt diff --git a/fastlane/metadata/android/en-US/changelogs/40103170.txt b/fastlane/metadata/android/en-US/changelogs/40103170.txt new file mode 100644 index 0000000000..2d0062bb23 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103170.txt @@ -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 \ No newline at end of file From 7dad08654b63ed85a96072c791a99b63be8d9300 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 14:48:54 +0100 Subject: [PATCH 75/77] Revert "Downgrade the version to 1.3.17 to prepare a corrective release." This reverts commit 2efa67e587aafac380161c2132def05c2548feac. --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 7b33bdd9d6..a4102f7441 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.3.17\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.18\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" diff --git a/vector/build.gradle b/vector/build.gradle index 1d880736f8..8792f526d4 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -18,7 +18,7 @@ ext.versionMinor = 3 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 17 +ext.versionPatch = 18 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 78f09e25ae31e35d61e72997644a875c5342eb07 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 15:50:16 +0100 Subject: [PATCH 76/77] Give more visibility to the sanity test nighly by adding a badge to the README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dedc9da2dd..2e6c812703 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-androi [Get it on Google Play](https://play.google.com/store/apps/details?id=im.vector.app) [Get it on F-Droid](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) + # New Android SDK From cd1693548a37cb5f3e294cdaeaebf9d365ed3557 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 31 Jan 2022 16:03:21 +0100 Subject: [PATCH 77/77] With the link... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e6c812703..d784841e2c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-androi [Get it on Google Play](https://play.google.com/store/apps/details?id=im.vector.app) [Get it on F-Droid](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) Nighly sanity test status: ![allScreensTest](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml/badge.svg) +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