Merge pull request #6517 from vector-im/feature/adm/build-config

Avoiding direct `BuildConfig` usage
This commit is contained in:
Adam Brown 2022-08-02 18:07:17 +01:00 committed by GitHub
commit 8aaf185a8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 607 additions and 268 deletions

1
changelog.d/6406.misc Normal file
View File

@ -0,0 +1 @@
[Modularization] Provides abstraction to avoids direct usages of BuildConfig

View File

@ -0,0 +1,48 @@
/*
* 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.config
/**
* The types of analytics Element currently supports.
*/
sealed interface Analytics {
/**
* Disables the analytics integrations.
*/
object Disabled : Analytics
/**
* Analytics integration via PostHog.
*/
data class PostHog(
/**
* The PostHog instance url.
*/
val postHogHost: String,
/**
* The PostHog instance API key.
*/
val postHogApiKey: String,
/**
* A URL to more information about the analytics collection.
*/
val policyLink: String,
) : Analytics
}

View File

@ -36,4 +36,57 @@ object Config {
* - Changing the value from `true` to `false` will force the app to return to the background sync / Firebase Push. * - Changing the value from `true` to `false` will force the app to return to the background sync / Firebase Push.
*/ */
const val ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS = true const val ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS = true
const val ENABLE_LOCATION_SHARING = true
const val LOCATION_MAP_TILER_KEY = "fU3vlMsMn4Jb6dnEIFsx"
/**
* The maximum length of voice messages in milliseconds.
*/
const val VOICE_MESSAGE_LIMIT_MS = 120_000L
/**
* The strategy for sharing device keys.
*/
val KEY_SHARING_STRATEGY = KeySharingStrategy.WhenTyping
/**
* The onboarding flow.
*/
val ONBOARDING_VARIANT = OnboardingVariant.FTUE_AUTH
/**
* If set, MSC3086 asserted identity messages sent on VoIP calls will cause the call to appear in the room corresponding to the asserted identity.
* This *must* only be set in trusted environments.
*/
const val HANDLE_CALL_ASSERTED_IDENTITY_EVENTS = false
const val LOW_PRIVACY_LOG_ENABLE = false
const val ENABLE_STRICT_MODE_LOGS = false
/**
* The analytics configuration to use for the Debug build type.
* Can be disabled by providing Analytics.Disabled
*/
val DEBUG_ANALYTICS_CONFIG = Analytics.PostHog(
postHogHost = "https://posthog.element.dev",
postHogApiKey = "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN",
policyLink = "https://element.io/cookie-policy",
)
/**
* The analytics configuration to use for the Release build type.
* Can be disabled by providing Analytics.Disabled
*/
val RELEASE_ANALYTICS_CONFIG = Analytics.PostHog(
postHogHost = "https://posthog.hss.element.io",
postHogApiKey = "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO",
policyLink = "https://element.io/cookie-policy",
)
/**
* The analytics configuration to use for the Nightly build type.
* Can be disabled by providing Analytics.Disabled
*/
val NIGHTLY_ANALYTICS_CONFIG = RELEASE_ANALYTICS_CONFIG
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 New Vector Ltd * Copyright (c) 2022 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,12 +16,20 @@
package im.vector.app.config package im.vector.app.config
import im.vector.app.BuildConfig enum class KeySharingStrategy {
import im.vector.app.features.analytics.AnalyticsConfig /**
* Keys will be sent for the first time when the first message is sent.
* This is handled by the Matrix SDK so there's no need to do it in Vector.
*/
WhenSendingEvent,
val analyticsConfig: AnalyticsConfig = object : AnalyticsConfig { /**
override val isEnabled = BuildConfig.APPLICATION_ID == "im.vector.app.debug" * Keys will be sent for the first time when the timeline displayed.
override val postHogHost = "https://posthog.element.dev" */
override val postHogApiKey = "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN" WhenEnteringRoom,
override val policyLink = "https://element.io/cookie-policy"
/**
* Keys will be sent for the first time when a typing started.
*/
WhenTyping
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 New Vector Ltd * Copyright (c) 2022 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,17 +16,8 @@
package im.vector.app.config package im.vector.app.config
import im.vector.app.BuildConfig enum class OnboardingVariant {
import im.vector.app.features.analytics.AnalyticsConfig LEGACY,
LOGIN_2,
private val allowedPackageList = listOf( FTUE_AUTH
"im.vector.app",
"im.vector.app.nightly",
)
val analyticsConfig: AnalyticsConfig = object : AnalyticsConfig {
override val isEnabled = BuildConfig.APPLICATION_ID in allowedPackageList
override val postHogHost = "https://posthog.hss.element.io"
override val postHogApiKey = "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO"
override val policyLink = "https://element.io/cookie-policy"
} }

View File

@ -156,19 +156,6 @@ android {
buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\"" buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\""
buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\"" buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\""
buildConfigField "im.vector.app.features.VectorFeatures.OnboardingVariant", "ONBOARDING_VARIANT", "im.vector.app.features.VectorFeatures.OnboardingVariant.FTUE_AUTH"
buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping"
buildConfigField "Long", "VOICE_MESSAGE_DURATION_LIMIT_MS", "120_000L"
// If set, MSC3086 asserted identity messages sent on VoIP calls will cause the call to appear in the room corresponding to the asserted identity.
// This *must* only be set in trusted environments.
buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false"
buildConfigField "Boolean", "enableLocationSharing", "true"
buildConfigField "String", "mapTilerKey", "\"fU3vlMsMn4Jb6dnEIFsx\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// Keep abiFilter for the universalApk // Keep abiFilter for the universalApk
@ -250,10 +237,6 @@ android {
resValue "string", "app_name", "Element dbg" resValue "string", "app_name", "Element dbg"
resValue "color", "launcher_background", "#0DBD8B" resValue "color", "launcher_background", "#0DBD8B"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
// Set to true if you want to enable strict mode in debug
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
if (project.hasProperty("coverage")) { if (project.hasProperty("coverage")) {
@ -265,10 +248,6 @@ android {
resValue "string", "app_name", "Element" resValue "string", "app_name", "Element"
resValue "color", "launcher_background", "#0DBD8B" resValue "color", "launcher_background", "#0DBD8B"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
// When updating this block, please also update the same block in the `nightly` buildType below
postprocessing { postprocessing {
removeUnusedCode true removeUnusedCode true
removeUnusedResources true removeUnusedResources true
@ -329,7 +308,6 @@ android {
versionName "${versionMajor}.${versionMinor}.${versionPatch}${getGplayVersionSuffix()}" versionName "${versionMajor}.${versionMinor}.${versionPatch}${getGplayVersionSuffix()}"
resValue "bool", "isGplay", "true" resValue "bool", "isGplay", "true"
buildConfigField "boolean", "ALLOW_FCM_USE", "true"
buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"G\"" buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"G\""
buildConfigField "String", "FLAVOR_DESCRIPTION", "\"GooglePlay\"" buildConfigField "String", "FLAVOR_DESCRIPTION", "\"GooglePlay\""
} }
@ -340,7 +318,6 @@ android {
versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}" versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}"
resValue "bool", "isGplay", "false" resValue "bool", "isGplay", "false"
buildConfigField "boolean", "ALLOW_FCM_USE", "false"
buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"F\"" buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"F\""
buildConfigField "String", "FLAVOR_DESCRIPTION", "\"FDroid\"" buildConfigField "String", "FLAVOR_DESCRIPTION", "\"FDroid\""
} }

View File

@ -70,6 +70,11 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.allowExternalUnifiedPushDistributors, key = DebugFeatureKeys.allowExternalUnifiedPushDistributors,
factory = VectorFeatures::allowExternalUnifiedPushDistributors factory = VectorFeatures::allowExternalUnifiedPushDistributors
), ),
createBooleanFeature(
label = "Enable Live Location Sharing",
key = DebugFeatureKeys.liveLocationSharing,
factory = VectorFeatures::isLocationSharingEnabled
),
createBooleanFeature( createBooleanFeature(
label = "Force usage of OpusEncoder library", label = "Force usage of OpusEncoder library",
key = DebugFeatureKeys.forceUsageOfOpusEncoder, key = DebugFeatureKeys.forceUsageOfOpusEncoder,

View File

@ -24,6 +24,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import im.vector.app.config.OnboardingVariant
import im.vector.app.features.DefaultVectorFeatures import im.vector.app.features.DefaultVectorFeatures
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -39,8 +40,8 @@ class DebugVectorFeatures(
private val dataStore = context.dataStore private val dataStore = context.dataStore
override fun onboardingVariant(): VectorFeatures.OnboardingVariant { override fun onboardingVariant(): OnboardingVariant {
return readPreferences().getEnum<VectorFeatures.OnboardingVariant>() ?: vectorFeatures.onboardingVariant() return readPreferences().getEnum<OnboardingVariant>() ?: vectorFeatures.onboardingVariant()
} }
override fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.onboardingAlreadyHaveAnAccount) override fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.onboardingAlreadyHaveAnAccount)
@ -66,6 +67,9 @@ class DebugVectorFeatures(
override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing) override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
?: vectorFeatures.isScreenSharingEnabled() ?: vectorFeatures.isScreenSharingEnabled()
override fun isLocationSharingEnabled(): Boolean = read(DebugFeatureKeys.liveLocationSharing)
?: vectorFeatures.isLocationSharingEnabled()
override fun forceUsageOfOpusEncoder(): Boolean = read(DebugFeatureKeys.forceUsageOfOpusEncoder) override fun forceUsageOfOpusEncoder(): Boolean = read(DebugFeatureKeys.forceUsageOfOpusEncoder)
?: vectorFeatures.forceUsageOfOpusEncoder() ?: vectorFeatures.forceUsageOfOpusEncoder()

View File

@ -40,7 +40,9 @@ import com.mapbox.mapboxsdk.Mapbox
import com.vanniktech.emoji.EmojiManager import com.vanniktech.emoji.EmojiManager
import com.vanniktech.emoji.google.GoogleEmojiProvider import com.vanniktech.emoji.google.GoogleEmojiProvider
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import im.vector.app.config.Config
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.configuration.VectorConfiguration
@ -99,6 +101,7 @@ class VectorApplication :
@Inject lateinit var flipperProxy: FlipperProxy @Inject lateinit var flipperProxy: FlipperProxy
@Inject lateinit var matrix: Matrix @Inject lateinit var matrix: Matrix
@Inject lateinit var fcmHelper: FcmHelper @Inject lateinit var fcmHelper: FcmHelper
@Inject lateinit var buildMeta: BuildMeta
// font thread handler // font thread handler
private var fontThreadHandler: Handler? = null private var fontThreadHandler: Handler? = null
@ -127,12 +130,12 @@ class VectorApplication :
.filterIsInstance(JitsiMeetDefaultLogHandler::class.java) .filterIsInstance(JitsiMeetDefaultLogHandler::class.java)
.forEach { Timber.uproot(it) } .forEach { Timber.uproot(it) }
if (BuildConfig.DEBUG) { if (buildMeta.isDebug) {
Timber.plant(Timber.DebugTree()) Timber.plant(Timber.DebugTree())
} }
Timber.plant(vectorFileLogger) Timber.plant(vectorFileLogger)
if (BuildConfig.DEBUG) { if (buildMeta.isDebug) {
Stetho.initializeWithDefaults(this) Stetho.initializeWithDefaults(this)
} }
logInfo() logInfo()
@ -148,7 +151,7 @@ class VectorApplication :
R.array.com_google_android_gms_fonts_certs R.array.com_google_android_gms_fonts_certs
) )
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler()) FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
VectorLocale.init(this) VectorLocale.init(this, buildMeta)
ThemeUtils.init(this) ThemeUtils.init(this)
vectorConfiguration.applyToApplicationContext() vectorConfiguration.applyToApplicationContext()
@ -196,7 +199,7 @@ class VectorApplication :
} }
private fun enableStrictModeIfNeeded() { private fun enableStrictModeIfNeeded() {
if (BuildConfig.ENABLE_STRICT_MODE_LOGS) { if (Config.ENABLE_STRICT_MODE_LOGS) {
StrictMode.setThreadPolicy( StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder() StrictMode.ThreadPolicy.Builder()
.detectAll() .detectAll()

View File

@ -0,0 +1,80 @@
/*
* 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.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import im.vector.app.BuildConfig
import im.vector.app.config.Analytics
import im.vector.app.config.Config
import im.vector.app.config.KeySharingStrategy
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.call.webrtc.VoipConfig
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageConfig
import im.vector.app.features.location.LocationSharingConfig
import im.vector.app.features.raw.wellknown.CryptoConfig
@InstallIn(SingletonComponent::class)
@Module
object ConfigurationModule {
@Provides
fun providesAnalyticsConfig(): AnalyticsConfig {
val config: Analytics = when (BuildConfig.BUILD_TYPE) {
"debug" -> Config.DEBUG_ANALYTICS_CONFIG
"nightly" -> Config.NIGHTLY_ANALYTICS_CONFIG
"release" -> Config.RELEASE_ANALYTICS_CONFIG
else -> throw IllegalStateException("Unhandled build type: ${BuildConfig.BUILD_TYPE}")
}
return when (config) {
Analytics.Disabled -> AnalyticsConfig(isEnabled = false, "", "", "")
is Analytics.PostHog -> AnalyticsConfig(
isEnabled = true,
postHogHost = config.postHogHost,
postHogApiKey = config.postHogApiKey,
policyLink = config.policyLink
)
}
}
@Provides
fun providesVoiceMessageConfig() = VoiceMessageConfig(
lengthLimitMs = Config.VOICE_MESSAGE_LIMIT_MS
)
@Provides
fun providesCryptoConfig() = CryptoConfig(
fallbackKeySharingStrategy = when (Config.KEY_SHARING_STRATEGY) {
KeySharingStrategy.WhenSendingEvent -> OutboundSessionKeySharingStrategy.WhenSendingEvent
KeySharingStrategy.WhenEnteringRoom -> OutboundSessionKeySharingStrategy.WhenSendingEvent
KeySharingStrategy.WhenTyping -> OutboundSessionKeySharingStrategy.WhenSendingEvent
}
)
@Provides
fun providesLocationSharingConfig() = LocationSharingConfig(
mapTilerKey = Config.LOCATION_MAP_TILER_KEY,
)
@Provides
fun providesVoipConfig() = VoipConfig(
handleCallAssertedIdentityEvents = Config.HANDLE_CALL_ASSERTED_IDENTITY_EVENTS
)
}

View File

@ -33,7 +33,7 @@ import im.vector.app.EmojiCompatWrapper
import im.vector.app.EmojiSpanify import im.vector.app.EmojiSpanify
import im.vector.app.SpaceStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.SpaceStateHandlerImpl import im.vector.app.SpaceStateHandlerImpl
import im.vector.app.config.analyticsConfig import im.vector.app.config.Config
import im.vector.app.core.dispatchers.CoroutineDispatchers import im.vector.app.core.dispatchers.CoroutineDispatchers
import im.vector.app.core.error.DefaultErrorFormatter import im.vector.app.core.error.DefaultErrorFormatter
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
@ -42,7 +42,6 @@ import im.vector.app.core.time.Clock
import im.vector.app.core.time.DefaultClock import im.vector.app.core.time.DefaultClock
import im.vector.app.core.utils.AndroidSystemSettingsProvider import im.vector.app.core.utils.AndroidSystemSettingsProvider
import im.vector.app.core.utils.SystemSettingsProvider import im.vector.app.core.utils.SystemSettingsProvider
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
@ -205,17 +204,23 @@ object VectorStaticModule {
return GlobalScope return GlobalScope
} }
@Provides
fun providesAnalyticsConfig(): AnalyticsConfig {
return analyticsConfig
}
@Provides @Provides
fun providesPhoneNumberUtil(): PhoneNumberUtil = PhoneNumberUtil.getInstance() fun providesPhoneNumberUtil(): PhoneNumberUtil = PhoneNumberUtil.getInstance()
@Provides @Provides
@Singleton @Singleton
fun providesBuildMeta() = BuildMeta() fun providesBuildMeta() = BuildMeta(
isDebug = BuildConfig.DEBUG,
applicationId = BuildConfig.APPLICATION_ID,
lowPrivacyLoggingEnabled = Config.LOW_PRIVACY_LOG_ENABLE,
versionName = BuildConfig.VERSION_NAME,
gitRevision = BuildConfig.GIT_REVISION,
gitRevisionDate = BuildConfig.GIT_REVISION_DATE,
gitBranchName = BuildConfig.GIT_BRANCH_NAME,
buildNumber = BuildConfig.BUILD_NUMBER,
flavorDescription = BuildConfig.FLAVOR_DESCRIPTION,
flavorShortDescription = BuildConfig.SHORT_FLAVOR_DESCRIPTION,
)
@Provides @Provides
@Singleton @Singleton

View File

@ -37,7 +37,7 @@ import androidx.datastore.preferences.core.Preferences
import dagger.hilt.EntryPoints import dagger.hilt.EntryPoints
import im.vector.app.core.datastore.dataStoreProvider import im.vector.app.core.datastore.dataStoreProvider
import im.vector.app.core.di.SingletonEntryPoint import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.resources.BuildMeta import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
import java.io.OutputStream import java.io.OutputStream
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -93,9 +93,9 @@ fun Context.safeOpenOutputStream(uri: Uri): OutputStream? {
*/ */
@Suppress("deprecation") @Suppress("deprecation")
@SuppressLint("NewApi") // false positive @SuppressLint("NewApi") // false positive
fun Context.inferNoConnectivity(buildMeta: BuildMeta): Boolean { fun Context.inferNoConnectivity(sdkIntProvider: BuildVersionSdkIntProvider): Boolean {
val connectivityManager = getSystemService<ConnectivityManager>()!! val connectivityManager = getSystemService<ConnectivityManager>()!!
return if (buildMeta.sdkInt > Build.VERSION_CODES.M) { return if (sdkIntProvider.get() > Build.VERSION_CODES.M) {
val networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) val networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
when { when {
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true -> false networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true -> false

View File

@ -54,7 +54,6 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.EntryPointAccessors import dagger.hilt.android.EntryPointAccessors
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.di.ActivityEntryPoint
@ -68,6 +67,7 @@ import im.vector.app.core.extensions.restart
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.AndroidSystemSettingsProvider import im.vector.app.core.utils.AndroidSystemSettingsProvider
import im.vector.app.core.utils.ToolbarConfig import im.vector.app.core.utils.ToolbarConfig
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
@ -157,11 +157,9 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
protected lateinit var bugReporter: BugReporter protected lateinit var bugReporter: BugReporter
private lateinit var pinLocker: PinLocker private lateinit var pinLocker: PinLocker
@Inject @Inject lateinit var rageShake: RageShake
lateinit var rageShake: RageShake @Inject lateinit var buildMeta: BuildMeta
@Inject lateinit var fontScalePreferences: FontScalePreferences
@Inject
lateinit var fontScalePreferences: FontScalePreferences
@Inject @Inject
lateinit var vectorFeatures: VectorFeatures lateinit var vectorFeatures: VectorFeatures
@ -422,7 +420,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
} }
DebugReceiver DebugReceiver
.getIntentFilter(this) .getIntentFilter(this)
.takeIf { BuildConfig.DEBUG } .takeIf { buildMeta.isDebug }
?.let { ?.let {
debugReceiver = DebugReceiver() debugReceiver = DebugReceiver()
registerReceiver(debugReceiver, it) registerReceiver(debugReceiver, it)

View File

@ -25,14 +25,14 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.BuildConfig
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.network.WifiDetector import im.vector.app.core.network.WifiDetector
import im.vector.app.core.pushers.model.PushData import im.vector.app.core.pushers.model.PushData
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.services.GuardServiceStarter
import im.vector.app.features.notifications.NotifiableEventResolver import im.vector.app.features.notifications.NotifiableEventResolver
import im.vector.app.features.notifications.NotificationActionIds
import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorDataStore
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
@ -68,6 +68,8 @@ class VectorMessagingReceiver : MessagingReceiver() {
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper @Inject lateinit var unifiedPushHelper: UnifiedPushHelper
@Inject lateinit var unifiedPushStore: UnifiedPushStore @Inject lateinit var unifiedPushStore: UnifiedPushStore
@Inject lateinit var pushParser: PushParser @Inject lateinit var pushParser: PushParser
@Inject lateinit var actionIds: NotificationActionIds
@Inject lateinit var buildMeta: BuildMeta
private val coroutineScope = CoroutineScope(SupervisorJob()) private val coroutineScope = CoroutineScope(SupervisorJob())
@ -87,7 +89,7 @@ class VectorMessagingReceiver : MessagingReceiver() {
Timber.tag(loggerTag.value).d("## onMessage() received") Timber.tag(loggerTag.value).d("## onMessage() received")
val sMessage = String(message) val sMessage = String(message)
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.tag(loggerTag.value).d("## onMessage() $sMessage") Timber.tag(loggerTag.value).d("## onMessage() $sMessage")
} }
@ -100,7 +102,7 @@ class VectorMessagingReceiver : MessagingReceiver() {
// Diagnostic Push // Diagnostic Push
if (pushData.eventId == PushersManager.TEST_EVENT_ID) { if (pushData.eventId == PushersManager.TEST_EVENT_ID) {
val intent = Intent(NotificationUtils.PUSH_ACTION) val intent = Intent(actionIds.push)
LocalBroadcastManager.getInstance(context).sendBroadcast(intent) LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
return return
} }
@ -171,7 +173,7 @@ class VectorMessagingReceiver : MessagingReceiver() {
*/ */
private suspend fun onMessageReceivedInternal(pushData: PushData) { private suspend fun onMessageReceivedInternal(pushData: PushData) {
try { try {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal() : $pushData") Timber.tag(loggerTag.value).d("## onMessageReceivedInternal() : $pushData")
} else { } else {
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal()") Timber.tag(loggerTag.value).d("## onMessageReceivedInternal()")

View File

@ -16,8 +16,15 @@
package im.vector.app.core.resources package im.vector.app.core.resources
import android.os.Build
data class BuildMeta( data class BuildMeta(
val sdkInt: Int = Build.VERSION.SDK_INT val isDebug: Boolean,
val applicationId: String,
val lowPrivacyLoggingEnabled: Boolean,
val versionName: String,
val gitRevision: String,
val gitRevisionDate: String,
val gitBranchName: String,
val buildNumber: String,
val flavorDescription: String,
val flavorShortDescription: String,
) )

View File

@ -39,7 +39,6 @@ import androidx.browser.customtabs.CustomTabsSession
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -182,7 +181,7 @@ fun openUri(activity: Activity, uri: String) {
*/ */
fun openMedia(activity: Activity, savedMediaPath: String, mimeType: String) { fun openMedia(activity: Activity, savedMediaPath: String, mimeType: String) {
val file = File(savedMediaPath) val file = File(savedMediaPath)
val uri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileProvider", file) val uri = FileProvider.getUriForFile(activity, activity.packageName + ".fileProvider", file)
val intent = Intent(Intent.ACTION_VIEW).apply { val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, mimeType) setDataAndType(uri, mimeType)
@ -214,7 +213,7 @@ fun openLocation(activity: Activity, latitude: Double, longitude: Double) {
fun shareMedia(context: Context, file: File, mediaMimeType: String?) { fun shareMedia(context: Context, file: File, mediaMimeType: String?) {
val mediaUri = try { val mediaUri = try {
FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", file) FileProvider.getUriForFile(context, context.packageName + ".fileProvider", file)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "onMediaAction Selected File cannot be shared") Timber.e(e, "onMediaAction Selected File cannot be shared")
return return
@ -376,7 +375,7 @@ private fun addToGallery(savedFile: File, mediaMimeType: String?, context: Conte
/** /**
* Open the play store to the provided application Id, default to this app. * Open the play store to the provided application Id, default to this app.
*/ */
fun openPlayStore(activity: Activity, appId: String = BuildConfig.APPLICATION_ID) { fun openPlayStore(activity: Activity, appId: String) {
try { try {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appId"))) activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appId")))
} catch (activityNotFoundException: ActivityNotFoundException) { } catch (activityNotFoundException: ActivityNotFoundException) {

View File

@ -16,8 +16,8 @@
package im.vector.app.features package im.vector.app.features
import im.vector.app.BuildConfig
import im.vector.app.config.Config import im.vector.app.config.Config
import im.vector.app.config.OnboardingVariant
interface VectorFeatures { interface VectorFeatures {
@ -30,19 +30,14 @@ interface VectorFeatures {
fun isOnboardingCombinedLoginEnabled(): Boolean fun isOnboardingCombinedLoginEnabled(): Boolean
fun allowExternalUnifiedPushDistributors(): Boolean fun allowExternalUnifiedPushDistributors(): Boolean
fun isScreenSharingEnabled(): Boolean fun isScreenSharingEnabled(): Boolean
fun isLocationSharingEnabled(): Boolean
fun forceUsageOfOpusEncoder(): Boolean fun forceUsageOfOpusEncoder(): Boolean
fun shouldStartDmOnFirstMessage(): Boolean fun shouldStartDmOnFirstMessage(): Boolean
fun isNewAppLayoutEnabled(): Boolean fun isNewAppLayoutEnabled(): Boolean
enum class OnboardingVariant {
LEGACY,
LOGIN_2,
FTUE_AUTH
}
} }
class DefaultVectorFeatures : VectorFeatures { class DefaultVectorFeatures : VectorFeatures {
override fun onboardingVariant(): VectorFeatures.OnboardingVariant = BuildConfig.ONBOARDING_VARIANT override fun onboardingVariant() = Config.ONBOARDING_VARIANT
override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true
override fun isOnboardingSplashCarouselEnabled() = true override fun isOnboardingSplashCarouselEnabled() = true
override fun isOnboardingUseCaseEnabled() = true override fun isOnboardingUseCaseEnabled() = true
@ -51,6 +46,7 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isOnboardingCombinedLoginEnabled() = true override fun isOnboardingCombinedLoginEnabled() = true
override fun allowExternalUnifiedPushDistributors(): Boolean = Config.ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS override fun allowExternalUnifiedPushDistributors(): Boolean = Config.ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS
override fun isScreenSharingEnabled(): Boolean = true override fun isScreenSharingEnabled(): Boolean = true
override fun isLocationSharingEnabled() = Config.ENABLE_LOCATION_SHARING
override fun forceUsageOfOpusEncoder(): Boolean = false override fun forceUsageOfOpusEncoder(): Boolean = false
override fun shouldStartDmOnFirstMessage(): Boolean = false override fun shouldStartDmOnFirstMessage(): Boolean = false
override fun isNewAppLayoutEnabled(): Boolean = false override fun isNewAppLayoutEnabled(): Boolean = false

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 New Vector Ltd * Copyright (c) 2022 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,9 @@
package im.vector.app.features.analytics package im.vector.app.features.analytics
interface AnalyticsConfig { data class AnalyticsConfig(
val isEnabled: Boolean val isEnabled: Boolean,
val postHogHost: String val postHogHost: String,
val postHogApiKey: String val postHogApiKey: String,
val policyLink: String val policyLink: String,
} )

View File

@ -18,11 +18,15 @@ package im.vector.app.features.analytics.impl
import android.content.Context import android.content.Context
import com.posthog.android.PostHog import com.posthog.android.PostHog
import im.vector.app.BuildConfig import im.vector.app.core.resources.BuildMeta
import im.vector.app.config.analyticsConfig import im.vector.app.features.analytics.AnalyticsConfig
import javax.inject.Inject import javax.inject.Inject
class PostHogFactory @Inject constructor(private val context: Context) { class PostHogFactory @Inject constructor(
private val context: Context,
private val analyticsConfig: AnalyticsConfig,
private val buildMeta: BuildMeta,
) {
fun createPosthog(): PostHog { fun createPosthog(): PostHog {
return PostHog.Builder(context, analyticsConfig.postHogApiKey, analyticsConfig.postHogHost) return PostHog.Builder(context, analyticsConfig.postHogApiKey, analyticsConfig.postHogHost)
@ -43,7 +47,7 @@ class PostHogFactory @Inject constructor(private val context: Context) {
} }
private fun getLogLevel(): PostHog.LogLevel { private fun getLogLevel(): PostHog.LogLevel {
return if (BuildConfig.DEBUG) { return if (buildMeta.isDebug) {
PostHog.LogLevel.DEBUG PostHog.LogLevel.DEBUG
} else { } else {
PostHog.LogLevel.INFO PostHog.LogLevel.INFO

View File

@ -22,17 +22,17 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import im.vector.app.R import im.vector.app.R
import im.vector.app.config.analyticsConfig
import im.vector.app.core.extensions.setTextWithColoredPart import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentAnalyticsOptinBinding import im.vector.app.databinding.FragmentAnalyticsOptinBinding
import im.vector.app.features.analytics.AnalyticsConfig
import javax.inject.Inject import javax.inject.Inject
class AnalyticsOptInFragment @Inject constructor() : class AnalyticsOptInFragment @Inject constructor(
VectorBaseFragment<FragmentAnalyticsOptinBinding>(), private val analyticsConfig: AnalyticsConfig,
OnBackPressed { ) : VectorBaseFragment<FragmentAnalyticsOptinBinding>(), OnBackPressed {
// Share the view model with the Activity so that the Activity // Share the view model with the Activity so that the Activity
// can decide what to do when the data has been saved // can decide what to do when the data has been saved

View File

@ -23,9 +23,9 @@ import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import im.vector.app.core.dialogs.PhotoOrVideoDialog import im.vector.app.core.dialogs.PhotoOrVideoDialog
import im.vector.app.core.platform.Restorable import im.vector.app.core.platform.Restorable
import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.lib.multipicker.MultiPicker import im.vector.lib.multipicker.MultiPicker
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import timber.log.Timber import timber.log.Timber
@ -35,15 +35,14 @@ private const val PENDING_TYPE_KEY = "PENDING_TYPE_KEY"
/** /**
* This class helps to handle attachments by providing simple methods. * This class helps to handle attachments by providing simple methods.
*/ */
class AttachmentsHelper(val context: Context, val callback: Callback) : Restorable { class AttachmentsHelper(
val context: Context,
val callback: Callback,
private val buildMeta: BuildMeta,
) : Restorable {
interface Callback { interface Callback {
fun onContactAttachmentReady(contactAttachment: ContactAttachment) { fun onContactAttachmentReady(contactAttachment: ContactAttachment)
if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.v("On contact attachment ready: $contactAttachment")
}
}
fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>)
} }
@ -144,6 +143,9 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab
.firstOrNull() .firstOrNull()
?.toContactAttachment() ?.toContactAttachment()
?.let { ?.let {
if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.v("On contact attachment ready: $it")
}
callback.onContactAttachmentReady(it) callback.onContactAttachmentReady(it)
} }
} }

View File

@ -0,0 +1,21 @@
/*
* 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.call.webrtc
data class VoipConfig(
val handleCallAssertedIdentityEvents: Boolean
)

View File

@ -20,7 +20,6 @@ import android.content.Context
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import im.vector.app.ActiveSessionDataSource import im.vector.app.ActiveSessionDataSource
import im.vector.app.BuildConfig
import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.core.services.CallAndroidService import im.vector.app.core.services.CallAndroidService
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
@ -74,6 +73,7 @@ class WebRtcCallManager @Inject constructor(
private val activeSessionDataSource: ActiveSessionDataSource, private val activeSessionDataSource: ActiveSessionDataSource,
private val analyticsTracker: AnalyticsTracker, private val analyticsTracker: AnalyticsTracker,
private val unifiedPushHelper: UnifiedPushHelper, private val unifiedPushHelper: UnifiedPushHelper,
private val voipConfig: VoipConfig,
) : CallListener, ) : CallListener,
DefaultLifecycleObserver { DefaultLifecycleObserver {
@ -444,7 +444,7 @@ class WebRtcCallManager @Inject constructor(
} }
override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
if (!BuildConfig.handleCallAssertedIdentityEvents) { if (!voipConfig.handleCallAssertedIdentityEvents) {
return return
} }
val call = callsByCallId[callAssertedIdentityContent.callId] val call = callsByCallId[callAssertedIdentityContent.callId]

View File

@ -22,11 +22,11 @@ import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.config.analyticsConfig
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.extensions.toAnalyticsType import im.vector.app.features.analytics.extensions.toAnalyticsType
import im.vector.app.features.analytics.plan.Signup import im.vector.app.features.analytics.plan.Signup
@ -80,7 +80,8 @@ class HomeActivityViewModel @AssistedInject constructor(
private val analyticsStore: AnalyticsStore, private val analyticsStore: AnalyticsStore,
private val lightweightSettingsStorage: LightweightSettingsStorage, private val lightweightSettingsStorage: LightweightSettingsStorage,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val analyticsTracker: AnalyticsTracker private val analyticsTracker: AnalyticsTracker,
private val analyticsConfig: AnalyticsConfig,
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) { ) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
@AssistedFactory @AssistedFactory

View File

@ -23,11 +23,11 @@ import android.view.ViewGroup
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.observeK import im.vector.app.core.extensions.observeK
import im.vector.app.core.extensions.replaceChildFragment import im.vector.app.core.extensions.replaceChildFragment
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentHomeDrawerBinding import im.vector.app.databinding.FragmentHomeDrawerBinding
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
@ -43,7 +43,8 @@ import javax.inject.Inject
class HomeDrawerFragment @Inject constructor( class HomeDrawerFragment @Inject constructor(
private val session: Session, private val session: Session,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val avatarRenderer: AvatarRenderer private val avatarRenderer: AvatarRenderer,
private val buildMeta: BuildMeta,
) : VectorBaseFragment<FragmentHomeDrawerBinding>() { ) : VectorBaseFragment<FragmentHomeDrawerBinding>() {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
@ -112,7 +113,7 @@ class HomeDrawerFragment @Inject constructor(
} }
// Debug menu // Debug menu
views.homeDrawerHeaderDebugView.isVisible = BuildConfig.DEBUG && vectorPreferences.developerMode() views.homeDrawerHeaderDebugView.isVisible = buildMeta.isDebug && vectorPreferences.developerMode()
views.homeDrawerHeaderDebugView.debouncedClicks { views.homeDrawerHeaderDebugView.debouncedClicks {
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
navigator.openDebug(requireActivity()) navigator.openDebug(requireActivity())

View File

@ -24,8 +24,8 @@ import androidx.annotation.WorkerThread
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import im.vector.app.BuildConfig
import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideApp
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.MainActivity import im.vector.app.features.MainActivity
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -35,13 +35,15 @@ import javax.inject.Inject
private val useAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O private val useAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
private const val adaptiveIconSizeDp = 108 private const val adaptiveIconSizeDp = 108
private const val adaptiveIconOuterSidesDp = 18 private const val adaptiveIconOuterSidesDp = 18
private const val directShareCategory = BuildConfig.APPLICATION_ID + ".SHORTCUT_SHARE"
class ShortcutCreator @Inject constructor( class ShortcutCreator @Inject constructor(
private val context: Context, private val context: Context,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val dimensionConverter: DimensionConverter private val dimensionConverter: DimensionConverter,
buildMeta: BuildMeta,
) { ) {
private val directShareCategory = buildMeta.applicationId + ".SHORTCUT_SHARE"
private val adaptiveIconSize = dimensionConverter.dpToPx(adaptiveIconSizeDp) private val adaptiveIconSize = dimensionConverter.dpToPx(adaptiveIconSizeDp)
private val adaptiveIconOuterSides = dimensionConverter.dpToPx(adaptiveIconOuterSidesDp) private val adaptiveIconOuterSides = dimensionConverter.dpToPx(adaptiveIconOuterSidesDp)
private val iconSize by lazy { private val iconSize by lazy {

View File

@ -67,7 +67,6 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiPopup
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.animations.play import im.vector.app.core.animations.play
import im.vector.app.core.dialogs.ConfirmationDialogBuilder import im.vector.app.core.dialogs.ConfirmationDialogBuilder
@ -92,6 +91,7 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.platform.lifecycleAwareLazy import im.vector.app.core.platform.lifecycleAwareLazy
import im.vector.app.core.platform.showOptimizedSnackbar import im.vector.app.core.platform.showOptimizedSnackbar
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.core.time.Clock import im.vector.app.core.time.Clock
@ -125,6 +125,7 @@ import im.vector.app.core.utils.startInstallFromSourceIntent
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.DialogReportContentBinding import im.vector.app.databinding.DialogReportContentBinding
import im.vector.app.databinding.FragmentTimelineBinding import im.vector.app.databinding.FragmentTimelineBinding
import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.extensions.toAnalyticsInteraction import im.vector.app.features.analytics.extensions.toAnalyticsInteraction
import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.analytics.plan.Interaction
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
@ -275,7 +276,9 @@ class TimelineFragment @Inject constructor(
private val callManager: WebRtcCallManager, private val callManager: WebRtcCallManager,
private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker, private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker,
private val shareIntentHandler: ShareIntentHandler, private val shareIntentHandler: ShareIntentHandler,
private val clock: Clock private val clock: Clock,
private val vectorFeatures: VectorFeatures,
private val buildMeta: BuildMeta,
) : ) :
VectorBaseFragment<FragmentTimelineBinding>(), VectorBaseFragment<FragmentTimelineBinding>(),
TimelineEventController.Callback, TimelineEventController.Callback,
@ -372,7 +375,7 @@ class TimelineFragment @Inject constructor(
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
sharedActivityActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java) sharedActivityActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java)
knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java) knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java)
attachmentsHelper = AttachmentsHelper(requireContext(), this).register() attachmentsHelper = AttachmentsHelper(requireContext(), this, buildMeta).register()
callActionsHandler = StartCallActionsHandler( callActionsHandler = StartCallActionsHandler(
roomId = timelineArgs.roomId, roomId = timelineArgs.roomId,
fragment = this, fragment = this,
@ -1559,7 +1562,7 @@ class TimelineFragment @Inject constructor(
attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@TimelineFragment) attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@TimelineFragment)
attachmentTypeSelector.setAttachmentVisibility( attachmentTypeSelector.setAttachmentVisibility(
AttachmentTypeSelectorView.Type.LOCATION, AttachmentTypeSelectorView.Type.LOCATION,
BuildConfig.enableLocationSharing vectorFeatures.isLocationSharingEnabled(),
) )
attachmentTypeSelector.setAttachmentVisibility( attachmentTypeSelector.setAttachmentVisibility(
AttachmentTypeSelectorView.Type.POLL, !isThreadTimeLine() AttachmentTypeSelectorView.Type.POLL, !isThreadTimeLine()
@ -2646,7 +2649,6 @@ class TimelineFragment @Inject constructor(
} }
override fun onContactAttachmentReady(contactAttachment: ContactAttachment) { override fun onContactAttachmentReady(contactAttachment: ContactAttachment) {
super.onContactAttachmentReady(contactAttachment)
val formattedContact = contactAttachment.toHumanReadable() val formattedContact = contactAttachment.toHumanReadable()
messageComposerViewModel.handle(MessageComposerAction.SendMessage(formattedContact, false)) messageComposerViewModel.handle(MessageComposerAction.SendMessage(formattedContact, false))
} }

View File

@ -33,6 +33,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.BehaviorDataSource import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
@ -56,6 +57,7 @@ import im.vector.app.features.location.live.StopLiveLocationShareUseCase
import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection
import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
import im.vector.app.features.raw.wellknown.CryptoConfig
import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault
import im.vector.app.features.raw.wellknown.withElementWellKnown import im.vector.app.features.raw.wellknown.withElementWellKnown
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
@ -137,6 +139,8 @@ class TimelineViewModel @AssistedInject constructor(
private val locationSharingServiceConnection: LocationSharingServiceConnection, private val locationSharingServiceConnection: LocationSharingServiceConnection,
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase, private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
private val redactLiveLocationShareEventUseCase: RedactLiveLocationShareEventUseCase, private val redactLiveLocationShareEventUseCase: RedactLiveLocationShareEventUseCase,
private val cryptoConfig: CryptoConfig,
buildMeta: BuildMeta,
timelineFactory: TimelineFactory, timelineFactory: TimelineFactory,
spaceStateHandler: SpaceStateHandler, spaceStateHandler: SpaceStateHandler,
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), ) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
@ -150,7 +154,7 @@ class TimelineViewModel @AssistedInject constructor(
val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId, initialState.rootThreadEventId) val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId, initialState.rootThreadEventId)
// Same lifecycle than the ViewModel (survive to screen rotation) // Same lifecycle than the ViewModel (survive to screen rotation)
val previewUrlRetriever = PreviewUrlRetriever(session, viewModelScope) val previewUrlRetriever = PreviewUrlRetriever(session, viewModelScope, buildMeta)
// Slot to keep a pending action during permission request // Slot to keep a pending action during permission request
var pendingAction: RoomDetailAction? = null var pendingAction: RoomDetailAction? = null
@ -205,7 +209,7 @@ class TimelineViewModel @AssistedInject constructor(
// Ensure to share the outbound session keys with all members // Ensure to share the outbound session keys with all members
if (room.roomCryptoService().isEncrypted()) { if (room.roomCryptoService().isEncrypted()) {
rawService.withElementWellKnown(viewModelScope, session.sessionParams) { rawService.withElementWellKnown(viewModelScope, session.sessionParams) {
val strategy = it.getOutboundSessionKeySharingStrategyOrDefault() val strategy = it.getOutboundSessionKeySharingStrategyOrDefault(cryptoConfig.fallbackKeySharingStrategy)
if (strategy == OutboundSessionKeySharingStrategy.WhenEnteringRoom) { if (strategy == OutboundSessionKeySharingStrategy.WhenEnteringRoom) {
prepareForEncryption() prepareForEncryption()
} }
@ -688,7 +692,7 @@ class TimelineViewModel @AssistedInject constructor(
// Ensure outbound session keys // Ensure outbound session keys
if (room.roomCryptoService().isEncrypted()) { if (room.roomCryptoService().isEncrypted()) {
rawService.withElementWellKnown(viewModelScope, session.sessionParams) { rawService.withElementWellKnown(viewModelScope, session.sessionParams) {
val strategy = it.getOutboundSessionKeySharingStrategyOrDefault() val strategy = it.getOutboundSessionKeySharingStrategyOrDefault(cryptoConfig.fallbackKeySharingStrategy)
if (strategy == OutboundSessionKeySharingStrategy.WhenTyping && action.focused) { if (strategy == OutboundSessionKeySharingStrategy.WhenTyping && action.focused) {
// Should we add some rate limit here, or do it only once per model lifecycle? // Should we add some rate limit here, or do it only once per model lifecycle?
prepareForEncryption() prepareForEncryption()

View File

@ -20,7 +20,7 @@ import android.content.Context
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.MediaPlayer import android.media.MediaPlayer
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import im.vector.app.BuildConfig import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
import im.vector.app.features.voice.VoiceFailure import im.vector.app.features.voice.VoiceFailure
import im.vector.app.features.voice.VoiceRecorder import im.vector.app.features.voice.VoiceRecorder
@ -43,6 +43,7 @@ import javax.inject.Inject
class AudioMessageHelper @Inject constructor( class AudioMessageHelper @Inject constructor(
private val context: Context, private val context: Context,
private val playbackTracker: AudioMessagePlaybackTracker, private val playbackTracker: AudioMessagePlaybackTracker,
private val buildMeta: BuildMeta,
voiceRecorderProvider: VoiceRecorderProvider voiceRecorderProvider: VoiceRecorderProvider
) { ) {
private var mediaPlayer: MediaPlayer? = null private var mediaPlayer: MediaPlayer? = null
@ -88,7 +89,7 @@ class AudioMessageHelper @Inject constructor(
try { try {
voiceMessageFile?.let { voiceMessageFile?.let {
val outputFileUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", it, "Voice message.${it.extension}") val outputFileUri = FileProvider.getUriForFile(context, buildMeta.applicationId + ".fileProvider", it, "Voice message.${it.extension}")
return outputFileUri return outputFileUri
.toMultiPickerAudioType(context) .toMultiPickerAudioType(context)
?.apply { ?.apply {

View File

@ -0,0 +1,21 @@
/*
* 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.home.room.detail.composer.voice
data class VoiceMessageConfig(
val lengthLimitMs: Long
)

View File

@ -21,7 +21,6 @@ import android.util.AttributeSet
import android.view.View import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.hardware.vibrate import im.vector.app.core.hardware.vibrate
import im.vector.app.core.time.Clock import im.vector.app.core.time.Clock
@ -57,6 +56,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
} }
@Inject lateinit var clock: Clock @Inject lateinit var clock: Clock
@Inject lateinit var voiceMessageConfig: VoiceMessageConfig
// We need to define views as lateinit var to be able to check if initialized for the bug fix on api 21 and 22. // We need to define views as lateinit var to be able to check if initialized for the bug fix on api 21 and 22.
@Suppress("UNNECESSARY_LATEINIT") @Suppress("UNNECESSARY_LATEINIT")
@ -202,7 +202,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
private fun onRecordingTick(isLocked: Boolean, milliseconds: Long) { private fun onRecordingTick(isLocked: Boolean, milliseconds: Long) {
voiceMessageViews.renderRecordingTimer(isLocked, milliseconds / 1_000) voiceMessageViews.renderRecordingTimer(isLocked, milliseconds / 1_000)
val timeDiffToRecordingLimit = BuildConfig.VOICE_MESSAGE_DURATION_LIMIT_MS - milliseconds val timeDiffToRecordingLimit = voiceMessageConfig.lengthLimitMs - milliseconds
if (timeDiffToRecordingLimit <= 0) { if (timeDiffToRecordingLimit <= 0) {
post { post {
callback.onRecordingLimitReached() callback.onRecordingLimitReached()

View File

@ -16,7 +16,7 @@
package im.vector.app.features.home.room.detail.timeline.url package im.vector.app.features.home.room.detail.timeline.url
import im.vector.app.BuildConfig import im.vector.app.core.resources.BuildMeta
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -27,7 +27,8 @@ import org.matrix.android.sdk.api.session.room.timeline.getLatestEventId
class PreviewUrlRetriever( class PreviewUrlRetriever(
session: Session, session: Session,
private val coroutineScope: CoroutineScope private val coroutineScope: CoroutineScope,
private val buildMeta: BuildMeta,
) { ) {
private val mediaService = session.mediaService() private val mediaService = session.mediaService()
@ -77,7 +78,7 @@ class PreviewUrlRetriever(
mediaService.getPreviewUrl( mediaService.getPreviewUrl(
url = urlToRetrieve, url = urlToRetrieve,
timestamp = null, timestamp = null,
cacheStrategy = if (BuildConfig.DEBUG) CacheStrategy.NoCache else CacheStrategy.TtlCache(CACHE_VALIDITY, false) cacheStrategy = if (buildMeta.isDebug) CacheStrategy.NoCache else CacheStrategy.TtlCache(CACHE_VALIDITY, false)
) )
}.fold( }.fold(
{ {

View File

@ -0,0 +1,21 @@
/*
* 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
data class LocationSharingConfig(
val mapTilerKey: String,
)

View File

@ -24,8 +24,8 @@ import androidx.annotation.RequiresPermission
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.location.LocationListenerCompat import androidx.core.location.LocationListenerCompat
import im.vector.app.BuildConfig
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
@ -40,7 +40,8 @@ import javax.inject.Singleton
@Singleton @Singleton
class LocationTracker @Inject constructor( class LocationTracker @Inject constructor(
context: Context, context: Context,
private val activeSessionHolder: ActiveSessionHolder private val activeSessionHolder: ActiveSessionHolder,
private val buildMeta: BuildMeta,
) : LocationListenerCompat { ) : LocationListenerCompat {
private val locationManager = context.getSystemService<LocationManager>() private val locationManager = context.getSystemService<LocationManager>()
@ -104,7 +105,7 @@ class LocationTracker @Inject constructor(
} }
.maxByOrNull { location -> location.time } .maxByOrNull { location -> location.time }
?.let { latestKnownLocation -> ?.let { latestKnownLocation ->
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.d("lastKnownLocation: $latestKnownLocation") Timber.d("lastKnownLocation: $latestKnownLocation")
} else { } else {
Timber.d("lastKnownLocation: ${latestKnownLocation.provider}") Timber.d("lastKnownLocation: ${latestKnownLocation.provider}")
@ -162,7 +163,7 @@ class LocationTracker @Inject constructor(
} }
override fun onLocationChanged(location: Location) { override fun onLocationChanged(location: Location) {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.d("onLocationChanged: $location") Timber.d("onLocationChanged: $location")
} else { } else {
Timber.d("onLocationChanged: ${location.provider}") Timber.d("onLocationChanged: ${location.provider}")
@ -196,7 +197,7 @@ class LocationTracker @Inject constructor(
private fun notifyLocation(location: Location) { private fun notifyLocation(location: Location) {
activeSessionHolder.getSafeActiveSession()?.coroutineScope?.launch { activeSessionHolder.getSafeActiveSession()?.coroutineScope?.launch {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.d("notify location: $location") Timber.d("notify location: $location")
} else { } else {
Timber.d("notify location: ${location.provider}") Timber.d("notify location: ${location.provider}")

View File

@ -16,7 +16,6 @@
package im.vector.app.features.location package im.vector.app.features.location
import im.vector.app.BuildConfig
import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.getElementWellknown
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.raw.RawService
@ -25,9 +24,10 @@ import javax.inject.Inject
class UrlMapProvider @Inject constructor( class UrlMapProvider @Inject constructor(
private val session: Session, private val session: Session,
private val rawService: RawService private val rawService: RawService,
locationSharingConfig: LocationSharingConfig,
) { ) {
private val keyParam = "?key=${BuildConfig.mapTilerKey}" private val keyParam = "?key=${locationSharingConfig.mapTilerKey}"
private val fallbackMapUrl = buildString { private val fallbackMapUrl = buildString {
append(MAP_BASE_URL) append(MAP_BASE_URL)

View File

@ -31,6 +31,7 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.location.live.map.LiveLocationMapViewActivity import im.vector.app.features.location.live.map.LiveLocationMapViewActivity
import im.vector.app.features.location.live.map.LiveLocationMapViewArgs import im.vector.app.features.location.live.map.LiveLocationMapViewArgs
import im.vector.app.features.notifications.NotificationActionIds
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import javax.inject.Inject import javax.inject.Inject
@ -41,6 +42,7 @@ class LiveLocationNotificationBuilder @Inject constructor(
private val context: Context, private val context: Context,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val clock: Clock, private val clock: Clock,
private val actionIds: NotificationActionIds,
) { ) {
/** /**
@ -66,7 +68,7 @@ class LiveLocationNotificationBuilder @Inject constructor(
liveLocationMapViewArgs = LiveLocationMapViewArgs(roomId = roomId), liveLocationMapViewArgs = LiveLocationMapViewArgs(roomId = roomId),
firstStartMainActivity = true firstStartMainActivity = true
) )
mapIntent.action = NotificationUtils.TAP_TO_VIEW_ACTION mapIntent.action = actionIds.tapToView
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
mapIntent.data = createIgnoredUri("openLiveLocationMap?$roomId") mapIntent.data = createIgnoredUri("openLiveLocationMap?$roomId")

View File

@ -27,9 +27,9 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentLoginServerUrlFormBinding import im.vector.app.databinding.FragmentLoginServerUrlFormBinding
@ -43,7 +43,9 @@ import javax.inject.Inject
/** /**
* In this screen, the user is prompted to enter a homeserver url. * In this screen, the user is prompted to enter a homeserver url.
*/ */
class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment<FragmentLoginServerUrlFormBinding>() { class LoginServerUrlFormFragment @Inject constructor(
private val buildMeta: BuildMeta,
) : AbstractLoginFragment<FragmentLoginServerUrlFormBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginServerUrlFormBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginServerUrlFormBinding {
return FragmentLoginServerUrlFormBinding.inflate(inflater, container, false) return FragmentLoginServerUrlFormBinding.inflate(inflater, container, false)
@ -99,7 +101,7 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment<F
views.loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_common_notice) views.loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_common_notice)
} }
} }
val completions = state.knownCustomHomeServersUrls + if (BuildConfig.DEBUG) listOf("http://10.0.2.2:8080") else emptyList() val completions = state.knownCustomHomeServersUrls + if (buildMeta.isDebug) listOf("http://10.0.2.2:8080") else emptyList()
views.loginServerUrlFormHomeServerUrl.setAdapter( views.loginServerUrlFormHomeServerUrl.setAdapter(
ArrayAdapter( ArrayAdapter(
requireContext(), requireContext(),

View File

@ -23,8 +23,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.BuildMeta
import im.vector.app.databinding.FragmentLoginSplashBinding import im.vector.app.databinding.FragmentLoginSplashBinding
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
@ -36,7 +36,8 @@ import javax.inject.Inject
* In this screen, the user is viewing an introduction to what he can do with this application. * In this screen, the user is viewing an introduction to what he can do with this application.
*/ */
class LoginSplashFragment @Inject constructor( class LoginSplashFragment @Inject constructor(
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences,
private val buildMeta: BuildMeta,
) : AbstractLoginFragment<FragmentLoginSplashBinding>() { ) : AbstractLoginFragment<FragmentLoginSplashBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplashBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplashBinding {
@ -57,12 +58,12 @@ class LoginSplashFragment @Inject constructor(
private fun setupViews() { private fun setupViews() {
views.loginSplashSubmit.debouncedClicks { getStarted() } views.loginSplashSubmit.debouncedClicks { getStarted() }
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) { if (buildMeta.isDebug || vectorPreferences.developerMode()) {
views.loginSplashVersion.isVisible = true views.loginSplashVersion.isVisible = true
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
views.loginSplashVersion.text = "Version : ${BuildConfig.VERSION_NAME}\n" + views.loginSplashVersion.text = "Version : ${buildMeta.versionName}\n" +
"Branch: ${BuildConfig.GIT_BRANCH_NAME}\n" + "Branch: ${buildMeta.gitBranchName}\n" +
"Build: ${BuildConfig.BUILD_NUMBER}" "Build: ${buildMeta.buildNumber}"
views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) } views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) }
} }
} }

View File

@ -26,9 +26,9 @@ import android.widget.ArrayAdapter
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.ensureProtocol
import im.vector.app.databinding.FragmentLoginServerUrlForm2Binding import im.vector.app.databinding.FragmentLoginServerUrlForm2Binding
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -43,7 +43,9 @@ import javax.net.ssl.HttpsURLConnection
/** /**
* In this screen, the user is prompted to enter a homeserver url. * In this screen, the user is prompted to enter a homeserver url.
*/ */
class LoginServerUrlFormFragment2 @Inject constructor() : AbstractLoginFragment2<FragmentLoginServerUrlForm2Binding>() { class LoginServerUrlFormFragment2 @Inject constructor(
private val buildMeta: BuildMeta,
) : AbstractLoginFragment2<FragmentLoginServerUrlForm2Binding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginServerUrlForm2Binding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginServerUrlForm2Binding {
return FragmentLoginServerUrlForm2Binding.inflate(inflater, container, false) return FragmentLoginServerUrlForm2Binding.inflate(inflater, container, false)
@ -80,7 +82,7 @@ class LoginServerUrlFormFragment2 @Inject constructor() : AbstractLoginFragment2
} }
private fun setupUi(state: LoginViewState2) { private fun setupUi(state: LoginViewState2) {
val completions = state.knownCustomHomeServersUrls + if (BuildConfig.DEBUG) listOf("http://10.0.2.2:8080") else emptyList() val completions = state.knownCustomHomeServersUrls + if (buildMeta.isDebug) listOf("http://10.0.2.2:8080") else emptyList()
views.loginServerUrlFormHomeServerUrl.setAdapter( views.loginServerUrlFormHomeServerUrl.setAdapter(
ArrayAdapter( ArrayAdapter(
requireContext(), requireContext(),

View File

@ -22,7 +22,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.BuildConfig import im.vector.app.core.resources.BuildMeta
import im.vector.app.databinding.FragmentLoginSplash2Binding import im.vector.app.databinding.FragmentLoginSplash2Binding
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import javax.inject.Inject import javax.inject.Inject
@ -32,7 +32,8 @@ import javax.inject.Inject
* This is the new splash screen. * This is the new splash screen.
*/ */
class LoginSplashSignUpSignInSelectionFragment2 @Inject constructor( class LoginSplashSignUpSignInSelectionFragment2 @Inject constructor(
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences,
private val buildMeta: BuildMeta,
) : AbstractLoginFragment2<FragmentLoginSplash2Binding>() { ) : AbstractLoginFragment2<FragmentLoginSplash2Binding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplash2Binding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplash2Binding {
@ -49,12 +50,12 @@ class LoginSplashSignUpSignInSelectionFragment2 @Inject constructor(
views.loginSignupSigninSignUp.setOnClickListener { signUp() } views.loginSignupSigninSignUp.setOnClickListener { signUp() }
views.loginSignupSigninSignIn.setOnClickListener { signIn() } views.loginSignupSigninSignIn.setOnClickListener { signIn() }
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) { if (buildMeta.isDebug || vectorPreferences.developerMode()) {
views.loginSplashVersion.isVisible = true views.loginSplashVersion.isVisible = true
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
views.loginSplashVersion.text = "Version : ${BuildConfig.VERSION_NAME}\n" + views.loginSplashVersion.text = "Version : ${buildMeta.versionName}\n" +
"Branch: ${BuildConfig.GIT_BRANCH_NAME}\n" + "Branch: ${buildMeta.gitBranchName}\n" +
"Build: ${BuildConfig.BUILD_NUMBER}" "Build: ${buildMeta.buildNumber}"
views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) } views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) }
} }
} }

View File

@ -33,10 +33,10 @@ import androidx.core.view.ViewCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R import im.vector.app.R
import im.vector.app.SpaceStateHandler import im.vector.app.SpaceStateHandler
import im.vector.app.config.OnboardingVariant
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.fatalError import im.vector.app.core.error.fatalError
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
import im.vector.app.features.VectorFeatures.OnboardingVariant
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.extensions.toAnalyticsViewRoom import im.vector.app.features.analytics.extensions.toAnalyticsViewRoom
import im.vector.app.features.analytics.plan.ViewRoom import im.vector.app.features.analytics.plan.ViewRoom

View File

@ -16,9 +16,9 @@
package im.vector.app.features.notifications package im.vector.app.features.notifications
import android.net.Uri import android.net.Uri
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.takeAs import im.vector.app.core.extensions.takeAs
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.time.Clock import im.vector.app.core.time.Clock
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
@ -62,6 +62,7 @@ class NotifiableEventResolver @Inject constructor(
private val noticeEventFormatter: NoticeEventFormatter, private val noticeEventFormatter: NoticeEventFormatter,
private val displayableEventFormatter: DisplayableEventFormatter, private val displayableEventFormatter: DisplayableEventFormatter,
private val clock: Clock, private val clock: Clock,
private val buildMeta: BuildMeta,
) { ) {
// private val eventDisplay = RiotEventDisplay(context) // private val eventDisplay = RiotEventDisplay(context)
@ -264,7 +265,7 @@ class NotifiableEventResolver @Inject constructor(
) )
} else { } else {
Timber.e("## unsupported notifiable event for eventId [${event.eventId}]") Timber.e("## unsupported notifiable event for eventId [${event.eventId}]")
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.e("## unsupported notifiable event for event [$event]") Timber.e("## unsupported notifiable event for event [$event]")
} }
// TODO generic handling? // TODO generic handling?

View File

@ -0,0 +1,41 @@
/*
* 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.notifications
import im.vector.app.core.resources.BuildMeta
import javax.inject.Inject
/**
* Util class for creating notifications.
* Note: Cannot inject ColorProvider in the constructor, because it requires an Activity
*/
data class NotificationActionIds @Inject constructor(
private val buildMeta: BuildMeta,
) {
val join = "${buildMeta.applicationId}.NotificationActions.JOIN_ACTION"
val reject = "${buildMeta.applicationId}.NotificationActions.REJECT_ACTION"
val quickLaunch = "${buildMeta.applicationId}.NotificationActions.QUICK_LAUNCH_ACTION"
val markRoomRead = "${buildMeta.applicationId}.NotificationActions.MARK_ROOM_READ_ACTION"
val smartReply = "${buildMeta.applicationId}.NotificationActions.SMART_REPLY_ACTION"
val dismissSummary = "${buildMeta.applicationId}.NotificationActions.DISMISS_SUMMARY_ACTION"
val dismissRoom = "${buildMeta.applicationId}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
val tapToView = "${buildMeta.applicationId}.NotificationActions.TAP_TO_VIEW_ACTION"
val diagnostic = "${buildMeta.applicationId}.NotificationActions.DIAGNOSTIC"
val push = "${buildMeta.applicationId}.PUSH"
}

View File

@ -48,31 +48,32 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var analyticsTracker: AnalyticsTracker @Inject lateinit var analyticsTracker: AnalyticsTracker
@Inject lateinit var clock: Clock @Inject lateinit var clock: Clock
@Inject lateinit var actionIds: NotificationActionIds
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return if (intent == null || context == null) return
Timber.v("NotificationBroadcastReceiver received : $intent") Timber.v("NotificationBroadcastReceiver received : $intent")
when (intent.action) { when (intent.action) {
NotificationUtils.SMART_REPLY_ACTION -> actionIds.smartReply ->
handleSmartReply(intent, context) handleSmartReply(intent, context)
NotificationUtils.DISMISS_ROOM_NOTIF_ACTION -> actionIds.dismissRoom ->
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId -> intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
notificationDrawerManager.updateEvents { it.clearMessagesForRoom(roomId) } notificationDrawerManager.updateEvents { it.clearMessagesForRoom(roomId) }
} }
NotificationUtils.DISMISS_SUMMARY_ACTION -> actionIds.dismissSummary ->
notificationDrawerManager.clearAllEvents() notificationDrawerManager.clearAllEvents()
NotificationUtils.MARK_ROOM_READ_ACTION -> actionIds.markRoomRead ->
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId -> intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
notificationDrawerManager.updateEvents { it.clearMessagesForRoom(roomId) } notificationDrawerManager.updateEvents { it.clearMessagesForRoom(roomId) }
handleMarkAsRead(roomId) handleMarkAsRead(roomId)
} }
NotificationUtils.JOIN_ACTION -> { actionIds.join -> {
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId -> intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(roomId) } notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(roomId) }
handleJoinRoom(roomId) handleJoinRoom(roomId)
} }
} }
NotificationUtils.REJECT_ACTION -> { actionIds.reject -> {
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId -> intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(roomId) } notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(roomId) }
handleRejectRoom(roomId) handleRejectRoom(roomId)

View File

@ -20,8 +20,8 @@ import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import im.vector.app.ActiveSessionDataSource import im.vector.app.ActiveSessionDataSource
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.FirstThrottler
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
@ -46,7 +46,8 @@ class NotificationDrawerManager @Inject constructor(
private val activeSessionDataSource: ActiveSessionDataSource, private val activeSessionDataSource: ActiveSessionDataSource,
private val notifiableEventProcessor: NotifiableEventProcessor, private val notifiableEventProcessor: NotifiableEventProcessor,
private val notificationRenderer: NotificationRenderer, private val notificationRenderer: NotificationRenderer,
private val notificationEventPersistence: NotificationEventPersistence private val notificationEventPersistence: NotificationEventPersistence,
private val buildMeta: BuildMeta,
) { ) {
private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY)
@ -92,7 +93,7 @@ class NotificationDrawerManager @Inject constructor(
} }
// If we support multi session, event list should be per userId // If we support multi session, event list should be per userId
// Currently only manage single session // Currently only manage single session
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.d("onNotifiableEventReceived(): $notifiableEvent") Timber.d("onNotifiableEventReceived(): $notifiableEvent")
} else { } else {
Timber.d("onNotifiableEventReceived(): is push: ${notifiableEvent.canBeReplaced}") Timber.d("onNotifiableEventReceived(): is push: ${notifiableEvent.canBeReplaced}")

View File

@ -45,7 +45,6 @@ import androidx.core.content.getSystemService
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.createIgnoredUri import im.vector.app.core.extensions.createIgnoredUri
import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.platform.PendingIntentCompat
@ -69,16 +68,13 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.random.Random import kotlin.random.Random
/**
* Util class for creating notifications.
* Note: Cannot inject ColorProvider in the constructor, because it requires an Activity
*/
@Singleton @Singleton
class NotificationUtils @Inject constructor( class NotificationUtils @Inject constructor(
private val context: Context, private val context: Context,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val clock: Clock, private val clock: Clock,
private val actionIds: NotificationActionIds,
) { ) {
companion object { companion object {
@ -94,21 +90,6 @@ class NotificationUtils @Inject constructor(
*/ */
const val NOTIFICATION_ID_FOREGROUND_SERVICE = 61 const val NOTIFICATION_ID_FOREGROUND_SERVICE = 61
/* ==========================================================================================
* IDs for actions
* ========================================================================================== */
const val JOIN_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.JOIN_ACTION"
const val REJECT_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.REJECT_ACTION"
private const val QUICK_LAUNCH_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.QUICK_LAUNCH_ACTION"
const val MARK_ROOM_READ_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.MARK_ROOM_READ_ACTION"
const val SMART_REPLY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.SMART_REPLY_ACTION"
const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION"
const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
const val DIAGNOSTIC_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DIAGNOSTIC"
const val PUSH_ACTION = "${BuildConfig.APPLICATION_ID}.PUSH"
/* ========================================================================================== /* ==========================================================================================
* IDs for channels * IDs for channels
* ========================================================================================== */ * ========================================================================================== */
@ -651,7 +632,7 @@ class NotificationUtils @Inject constructor(
// Add actions and notification intents // Add actions and notification intents
// Mark room as read // Mark room as read
val markRoomReadIntent = Intent(context, NotificationBroadcastReceiver::class.java) val markRoomReadIntent = Intent(context, NotificationBroadcastReceiver::class.java)
markRoomReadIntent.action = MARK_ROOM_READ_ACTION markRoomReadIntent.action = actionIds.markRoomRead
markRoomReadIntent.data = createIgnoredUri(roomInfo.roomId) markRoomReadIntent.data = createIgnoredUri(roomInfo.roomId)
markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId) markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
val markRoomReadPendingIntent = PendingIntent.getBroadcast( val markRoomReadPendingIntent = PendingIntent.getBroadcast(
@ -698,7 +679,7 @@ class NotificationUtils @Inject constructor(
val intent = Intent(context, NotificationBroadcastReceiver::class.java) val intent = Intent(context, NotificationBroadcastReceiver::class.java)
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId) intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
intent.action = DISMISS_ROOM_NOTIF_ACTION intent.action = actionIds.dismissRoom
val pendingIntent = PendingIntent.getBroadcast( val pendingIntent = PendingIntent.getBroadcast(
context.applicationContext, context.applicationContext,
clock.epochMillis().toInt(), clock.epochMillis().toInt(),
@ -733,7 +714,7 @@ class NotificationUtils @Inject constructor(
val roomId = inviteNotifiableEvent.roomId val roomId = inviteNotifiableEvent.roomId
// offer to type a quick reject button // offer to type a quick reject button
val rejectIntent = Intent(context, NotificationBroadcastReceiver::class.java) val rejectIntent = Intent(context, NotificationBroadcastReceiver::class.java)
rejectIntent.action = REJECT_ACTION rejectIntent.action = actionIds.reject
rejectIntent.data = createIgnoredUri("$roomId&$matrixId") rejectIntent.data = createIgnoredUri("$roomId&$matrixId")
rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
val rejectIntentPendingIntent = PendingIntent.getBroadcast( val rejectIntentPendingIntent = PendingIntent.getBroadcast(
@ -751,7 +732,7 @@ class NotificationUtils @Inject constructor(
// offer to type a quick accept button // offer to type a quick accept button
val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java) val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java)
joinIntent.action = JOIN_ACTION joinIntent.action = actionIds.join
joinIntent.data = createIgnoredUri("$roomId&$matrixId") joinIntent.data = createIgnoredUri("$roomId&$matrixId")
joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
val joinIntentPendingIntent = PendingIntent.getBroadcast( val joinIntentPendingIntent = PendingIntent.getBroadcast(
@ -834,7 +815,7 @@ class NotificationUtils @Inject constructor(
private fun buildOpenRoomIntent(roomId: String): PendingIntent? { private fun buildOpenRoomIntent(roomId: String): PendingIntent? {
val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId = roomId, switchToParentSpace = true), true) val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId = roomId, switchToParentSpace = true), true)
roomIntentTap.action = TAP_TO_VIEW_ACTION roomIntentTap.action = actionIds.tapToView
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
roomIntentTap.data = createIgnoredUri("openRoom?$roomId") roomIntentTap.data = createIgnoredUri("openRoom?$roomId")
@ -872,7 +853,7 @@ class NotificationUtils @Inject constructor(
val intent: Intent val intent: Intent
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent = Intent(context, NotificationBroadcastReceiver::class.java) intent = Intent(context, NotificationBroadcastReceiver::class.java)
intent.action = SMART_REPLY_ACTION intent.action = actionIds.smartReply
intent.data = createIgnoredUri(roomId) intent.data = createIgnoredUri(roomId)
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
return PendingIntent.getBroadcast( return PendingIntent.getBroadcast(
@ -948,7 +929,7 @@ class NotificationUtils @Inject constructor(
private fun getDismissSummaryPendingIntent(): PendingIntent { private fun getDismissSummaryPendingIntent(): PendingIntent {
val intent = Intent(context, NotificationBroadcastReceiver::class.java) val intent = Intent(context, NotificationBroadcastReceiver::class.java)
intent.action = DISMISS_SUMMARY_ACTION intent.action = actionIds.dismissSummary
intent.data = createIgnoredUri("deleteSummary") intent.data = createIgnoredUri("deleteSummary")
return PendingIntent.getBroadcast( return PendingIntent.getBroadcast(
context.applicationContext, context.applicationContext,
@ -987,7 +968,7 @@ class NotificationUtils @Inject constructor(
fun displayDiagnosticNotification() { fun displayDiagnosticNotification() {
val testActionIntent = Intent(context, TestNotificationReceiver::class.java) val testActionIntent = Intent(context, TestNotificationReceiver::class.java)
testActionIntent.action = DIAGNOSTIC_ACTION testActionIntent.action = actionIds.diagnostic
val testPendingIntent = PendingIntent.getBroadcast( val testPendingIntent = PendingIntent.getBroadcast(
context, context,
0, 0,

View File

@ -16,6 +16,7 @@
package im.vector.app.features.onboarding package im.vector.app.features.onboarding
import im.vector.app.config.OnboardingVariant
import im.vector.app.core.platform.ScreenOrientationLocker import im.vector.app.core.platform.ScreenOrientationLocker
import im.vector.app.databinding.ActivityLoginBinding import im.vector.app.databinding.ActivityLoginBinding
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
@ -34,8 +35,8 @@ class OnboardingVariantFactory @Inject constructor(
onboardingViewModel: Lazy<OnboardingViewModel>, onboardingViewModel: Lazy<OnboardingViewModel>,
loginViewModel2: Lazy<LoginViewModel2> loginViewModel2: Lazy<LoginViewModel2>
) = when (vectorFeatures.onboardingVariant()) { ) = when (vectorFeatures.onboardingVariant()) {
VectorFeatures.OnboardingVariant.LEGACY -> error("Legacy is not supported by the FTUE") OnboardingVariant.LEGACY -> error("Legacy is not supported by the FTUE")
VectorFeatures.OnboardingVariant.FTUE_AUTH -> FtueAuthVariant( OnboardingVariant.FTUE_AUTH -> FtueAuthVariant(
views = views, views = views,
onboardingViewModel = onboardingViewModel.value, onboardingViewModel = onboardingViewModel.value,
activity = activity, activity = activity,
@ -43,7 +44,7 @@ class OnboardingVariantFactory @Inject constructor(
vectorFeatures = vectorFeatures, vectorFeatures = vectorFeatures,
orientationLocker = orientationLocker orientationLocker = orientationLocker
) )
VectorFeatures.OnboardingVariant.LOGIN_2 -> Login2Variant( OnboardingVariant.LOGIN_2 -> Login2Variant(
views = views, views = views,
loginViewModel = loginViewModel2.value, loginViewModel = loginViewModel2.value,
activity = activity, activity = activity,

View File

@ -32,7 +32,6 @@ import im.vector.app.core.extensions.isMatrixId
import im.vector.app.core.extensions.toReducedUrl import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.core.extensions.vectorStore import im.vector.app.core.extensions.vectorStore
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.ensureTrailingSlash import im.vector.app.core.utils.ensureTrailingSlash
@ -63,6 +62,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
import java.util.concurrent.CancellationException import java.util.concurrent.CancellationException
@ -86,7 +86,7 @@ class OnboardingViewModel @AssistedInject constructor(
private val startAuthenticationFlowUseCase: StartAuthenticationFlowUseCase, private val startAuthenticationFlowUseCase: StartAuthenticationFlowUseCase,
private val vectorOverrides: VectorOverrides, private val vectorOverrides: VectorOverrides,
private val registrationActionHandler: RegistrationActionHandler, private val registrationActionHandler: RegistrationActionHandler,
private val buildMeta: BuildMeta, private val sdkIntProvider: BuildVersionSdkIntProvider,
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) { ) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
@AssistedFactory @AssistedFactory
@ -708,7 +708,7 @@ class OnboardingViewModel @AssistedInject constructor(
private fun onAuthenticationStartError(error: Throwable, trigger: OnboardingAction.HomeServerChange) { private fun onAuthenticationStartError(error: Throwable, trigger: OnboardingAction.HomeServerChange) {
when { when {
error.isHomeserverUnavailable() && applicationContext.inferNoConnectivity(buildMeta) -> _viewEvents.post( error.isHomeserverUnavailable() && applicationContext.inferNoConnectivity(sdkIntProvider) -> _viewEvents.post(
OnboardingViewEvents.Failure(error) OnboardingViewEvents.Failure(error)
) )
deeplinkUrlIsUnavailable(error, trigger) -> _viewEvents.post( deeplinkUrlIsUnavailable(error, trigger) -> _viewEvents.post(

View File

@ -27,9 +27,9 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentLoginServerUrlFormBinding import im.vector.app.databinding.FragmentLoginServerUrlFormBinding
@ -47,7 +47,9 @@ import javax.inject.Inject
/** /**
* In this screen, the user is prompted to enter a homeserver url. * In this screen, the user is prompted to enter a homeserver url.
*/ */
class FtueAuthServerUrlFormFragment @Inject constructor() : AbstractFtueAuthFragment<FragmentLoginServerUrlFormBinding>() { class FtueAuthServerUrlFormFragment @Inject constructor(
private val buildMeta: BuildMeta,
) : AbstractFtueAuthFragment<FragmentLoginServerUrlFormBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginServerUrlFormBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginServerUrlFormBinding {
return FragmentLoginServerUrlFormBinding.inflate(inflater, container, false) return FragmentLoginServerUrlFormBinding.inflate(inflater, container, false)
@ -103,7 +105,7 @@ class FtueAuthServerUrlFormFragment @Inject constructor() : AbstractFtueAuthFrag
views.loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_common_notice) views.loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_common_notice)
} }
} }
val completions = state.knownCustomHomeServersUrls + if (BuildConfig.DEBUG) listOf("http://10.0.2.2:8080") else emptyList() val completions = state.knownCustomHomeServersUrls + if (buildMeta.isDebug) listOf("http://10.0.2.2:8080") else emptyList()
views.loginServerUrlFormHomeServerUrl.setAdapter( views.loginServerUrlFormHomeServerUrl.setAdapter(
ArrayAdapter( ArrayAdapter(
requireContext(), requireContext(),

View File

@ -27,10 +27,10 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.incrementByOneAndWrap import im.vector.app.core.extensions.incrementByOneAndWrap
import im.vector.app.core.extensions.setCurrentItem import im.vector.app.core.extensions.setCurrentItem
import im.vector.app.core.resources.BuildMeta
import im.vector.app.databinding.FragmentFtueSplashCarouselBinding import im.vector.app.databinding.FragmentFtueSplashCarouselBinding
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingAction
@ -48,7 +48,8 @@ class FtueAuthSplashCarouselFragment @Inject constructor(
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val vectorFeatures: VectorFeatures, private val vectorFeatures: VectorFeatures,
private val carouselController: SplashCarouselController, private val carouselController: SplashCarouselController,
private val carouselStateFactory: SplashCarouselStateFactory private val carouselStateFactory: SplashCarouselStateFactory,
private val buildMeta: BuildMeta,
) : AbstractFtueAuthFragment<FragmentFtueSplashCarouselBinding>() { ) : AbstractFtueAuthFragment<FragmentFtueSplashCarouselBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueSplashCarouselBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueSplashCarouselBinding {
@ -76,11 +77,11 @@ class FtueAuthSplashCarouselFragment @Inject constructor(
debouncedClicks { alreadyHaveAnAccount() } debouncedClicks { alreadyHaveAnAccount() }
} }
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) { if (buildMeta.isDebug || vectorPreferences.developerMode()) {
views.loginSplashVersion.isVisible = true views.loginSplashVersion.isVisible = true
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
views.loginSplashVersion.text = "Version : ${BuildConfig.VERSION_NAME}#${BuildConfig.BUILD_NUMBER}\n" + views.loginSplashVersion.text = "Version : ${buildMeta.versionName}#${buildMeta.buildNumber}\n" +
"Branch: ${BuildConfig.GIT_BRANCH_NAME}" "Branch: ${buildMeta.gitBranchName}"
views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) } views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) }
} }
views.splashCarousel.registerAutomaticUntilInteractionTransitions() views.splashCarousel.registerAutomaticUntilInteractionTransitions()

View File

@ -22,8 +22,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.BuildMeta
import im.vector.app.databinding.FragmentFtueAuthSplashBinding import im.vector.app.databinding.FragmentFtueAuthSplashBinding
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingAction
@ -36,7 +36,8 @@ import javax.inject.Inject
*/ */
class FtueAuthSplashFragment @Inject constructor( class FtueAuthSplashFragment @Inject constructor(
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val vectorFeatures: VectorFeatures private val vectorFeatures: VectorFeatures,
private val buildMeta: BuildMeta,
) : AbstractFtueAuthFragment<FragmentFtueAuthSplashBinding>() { ) : AbstractFtueAuthFragment<FragmentFtueAuthSplashBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueAuthSplashBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueAuthSplashBinding {
@ -59,12 +60,12 @@ class FtueAuthSplashFragment @Inject constructor(
debouncedClicks { alreadyHaveAnAccount() } debouncedClicks { alreadyHaveAnAccount() }
} }
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) { if (buildMeta.isDebug || vectorPreferences.developerMode()) {
views.loginSplashVersion.isVisible = true views.loginSplashVersion.isVisible = true
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
views.loginSplashVersion.text = "Version : ${BuildConfig.VERSION_NAME}\n" + views.loginSplashVersion.text = "Version : ${buildMeta.versionName}\n" +
"Branch: ${BuildConfig.GIT_BRANCH_NAME}\n" + "Branch: ${buildMeta.gitBranchName}\n" +
"Build: ${BuildConfig.BUILD_NUMBER}" "Build: ${buildMeta.buildNumber}"
views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) } views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) }
} }
} }

View File

@ -30,6 +30,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.getAllChildFragments import im.vector.app.core.extensions.getAllChildFragments
import im.vector.app.core.extensions.toOnOff import im.vector.app.core.extensions.toOnOff
import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.settings.VectorLocale import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.devtools.GossipingEventsSerializer import im.vector.app.features.settings.devtools.GossipingEventsSerializer
@ -50,6 +51,7 @@ import okhttp3.Response
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.MatrixJsonParser import org.matrix.android.sdk.api.util.MatrixJsonParser
import org.matrix.android.sdk.api.util.MimeTypes import org.matrix.android.sdk.api.util.MimeTypes
@ -74,7 +76,9 @@ class BugReporter @Inject constructor(
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val vectorFileLogger: VectorFileLogger, private val vectorFileLogger: VectorFileLogger,
private val systemLocaleProvider: SystemLocaleProvider, private val systemLocaleProvider: SystemLocaleProvider,
private val matrix: Matrix private val matrix: Matrix,
private val buildMeta: BuildMeta,
private val sdkIntProvider: BuildVersionSdkIntProvider,
) { ) {
var inMultiWindowMode = false var inMultiWindowMode = false
@ -278,14 +282,14 @@ class BugReporter @Inject constructor(
.addFormDataPart("can_contact", canContact.toString()) .addFormDataPart("can_contact", canContact.toString())
.addFormDataPart("device_id", deviceId) .addFormDataPart("device_id", deviceId)
.addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false)) .addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false))
.addFormDataPart("branch_name", BuildConfig.GIT_BRANCH_NAME) .addFormDataPart("branch_name", buildMeta.gitBranchName)
.addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion())
.addFormDataPart("olm_version", olmVersion) .addFormDataPart("olm_version", olmVersion)
.addFormDataPart("device", Build.MODEL.trim()) .addFormDataPart("device", Build.MODEL.trim())
.addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff()) .addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff())
.addFormDataPart("multi_window", inMultiWindowMode.toOnOff()) .addFormDataPart("multi_window", inMultiWindowMode.toOnOff())
.addFormDataPart( .addFormDataPart(
"os", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ") " + "os", Build.VERSION.RELEASE + " (API " + sdkIntProvider.get() + ") " +
Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME
) )
.addFormDataPart("locale", Locale.getDefault().toString()) .addFormDataPart("locale", Locale.getDefault().toString())
@ -299,7 +303,7 @@ class BugReporter @Inject constructor(
} }
} }
val buildNumber = BuildConfig.BUILD_NUMBER val buildNumber = buildMeta.buildNumber
if (buildNumber.isNotEmpty() && buildNumber != "0") { if (buildNumber.isNotEmpty() && buildNumber != "0") {
builder.addFormDataPart("build_number", buildNumber) builder.addFormDataPart("build_number", buildNumber)
} }
@ -339,9 +343,9 @@ class BugReporter @Inject constructor(
screenshot = null screenshot = null
// add some github labels // add some github labels
builder.addFormDataPart("label", BuildConfig.VERSION_NAME) builder.addFormDataPart("label", buildMeta.versionName)
builder.addFormDataPart("label", BuildConfig.FLAVOR_DESCRIPTION) builder.addFormDataPart("label", buildMeta.flavorDescription)
builder.addFormDataPart("label", BuildConfig.GIT_BRANCH_NAME) builder.addFormDataPart("label", buildMeta.gitBranchName)
// Special for Element // Special for Element
builder.addFormDataPart("label", "[Element]") builder.addFormDataPart("label", "[Element]")

View File

@ -0,0 +1,23 @@
/*
* 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.raw.wellknown
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
data class CryptoConfig(
val fallbackKeySharingStrategy: OutboundSessionKeySharingStrategy
)

View File

@ -68,7 +68,7 @@ data class E2EWellKnownConfig(
val secureBackupSetupMethods: List<String>? = null, val secureBackupSetupMethods: List<String>? = null,
/** /**
* Configuration for sharing keys strategy which should be used instead of [im.vector.app.BuildConfig.outboundSessionKeySharingStrategy]. * Configuration for sharing keys strategy which should be used instead of [im.vector.app.config.Config.KEY_SHARING_STRATEGY].
* One of on_room_opening, on_typing or disabled. * One of on_room_opening, on_typing or disabled.
*/ */
@Json(name = "outbound_keys_pre_sharing_mode") @Json(name = "outbound_keys_pre_sharing_mode")

View File

@ -16,7 +16,6 @@
package im.vector.app.features.raw.wellknown package im.vector.app.features.raw.wellknown
import im.vector.app.BuildConfig
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -35,12 +34,12 @@ suspend fun RawService.getElementWellknown(sessionParams: SessionParams): Elemen
fun ElementWellKnown.isE2EByDefault() = elementE2E?.e2eDefault ?: riotE2E?.e2eDefault ?: true fun ElementWellKnown.isE2EByDefault() = elementE2E?.e2eDefault ?: riotE2E?.e2eDefault ?: true
fun ElementWellKnown?.getOutboundSessionKeySharingStrategyOrDefault(): OutboundSessionKeySharingStrategy { fun ElementWellKnown?.getOutboundSessionKeySharingStrategyOrDefault(fallback: OutboundSessionKeySharingStrategy): OutboundSessionKeySharingStrategy {
return when (this?.elementE2E?.outboundsKeyPreSharingMode) { return when (this?.elementE2E?.outboundsKeyPreSharingMode) {
"on_room_opening" -> OutboundSessionKeySharingStrategy.WhenEnteringRoom "on_room_opening" -> OutboundSessionKeySharingStrategy.WhenEnteringRoom
"on_typing" -> OutboundSessionKeySharingStrategy.WhenTyping "on_typing" -> OutboundSessionKeySharingStrategy.WhenTyping
"disabled" -> OutboundSessionKeySharingStrategy.WhenSendingEvent "disabled" -> OutboundSessionKeySharingStrategy.WhenSendingEvent
else -> BuildConfig.outboundSessionKeySharingStrategy else -> fallback
} }
} }

View File

@ -19,9 +19,9 @@ package im.vector.app.features.settings
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import androidx.core.content.edit import androidx.core.content.edit
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.core.resources.BuildMeta
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
@ -53,12 +53,14 @@ object VectorLocale {
private set private set
private lateinit var context: Context private lateinit var context: Context
private lateinit var buildMeta: BuildMeta
/** /**
* Init this object. * Init this object.
*/ */
fun init(context: Context) { fun init(context: Context, buildMeta: BuildMeta) {
this.context = context this.context = context
this.buildMeta = buildMeta
val preferences = DefaultSharedPreferences.getInstance(context) val preferences = DefaultSharedPreferences.getInstance(context)
if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) { if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) {
@ -174,7 +176,7 @@ object VectorLocale {
.setScript(script) .setScript(script)
.build() .build()
} catch (exception: IllformedLocaleException) { } catch (exception: IllformedLocaleException) {
if (BuildConfig.DEBUG) { if (buildMeta.isDebug) {
throw exception throw exception
} }
// Ignore this locale in production // Ignore this locale in production

View File

@ -23,9 +23,9 @@ import android.provider.MediaStore
import androidx.annotation.BoolRes import androidx.annotation.BoolRes
import androidx.core.content.edit import androidx.core.content.edit
import com.squareup.seismic.ShakeDetector import com.squareup.seismic.ShakeDetector
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.time.Clock import im.vector.app.core.time.Clock
import im.vector.app.features.disclaimer.SHARED_PREF_KEY import im.vector.app.features.disclaimer.SHARED_PREF_KEY
import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.home.ShortcutsHandler
@ -38,6 +38,7 @@ import javax.inject.Inject
class VectorPreferences @Inject constructor( class VectorPreferences @Inject constructor(
private val context: Context, private val context: Context,
private val clock: Clock, private val clock: Clock,
private val buildMeta: BuildMeta,
) { ) {
companion object { companion object {
@ -364,7 +365,7 @@ class VectorPreferences @Inject constructor(
} }
fun failFast(): Boolean { fun failFast(): Boolean {
return BuildConfig.DEBUG || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false)) return buildMeta.isDebug || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false))
} }
fun didAskUserToEnableSessionPush(): Boolean { fun didAskUserToEnableSessionPush(): Boolean {

View File

@ -18,10 +18,10 @@ package im.vector.app.features.settings
import android.os.Bundle import android.os.Bundle
import androidx.preference.Preference import androidx.preference.Preference
import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.orEmpty import im.vector.app.core.extensions.orEmpty
import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreference
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.FirstThrottler
import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.openAppSettingsPage import im.vector.app.core.utils.openAppSettingsPage
@ -32,7 +32,8 @@ import org.matrix.android.sdk.api.Matrix
import javax.inject.Inject import javax.inject.Inject
class VectorSettingsHelpAboutFragment @Inject constructor( class VectorSettingsHelpAboutFragment @Inject constructor(
private val versionProvider: VersionProvider private val versionProvider: VersionProvider,
private val buildMeta: BuildMeta,
) : VectorSettingsBaseFragment() { ) : VectorSettingsBaseFragment() {
override var titleRes = R.string.preference_root_help_about override var titleRes = R.string.preference_root_help_about
@ -66,9 +67,9 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
findPreference<VectorPreference>(VectorPreferences.SETTINGS_VERSION_PREFERENCE_KEY)!!.let { findPreference<VectorPreference>(VectorPreferences.SETTINGS_VERSION_PREFERENCE_KEY)!!.let {
it.summary = buildString { it.summary = buildString {
append(versionProvider.getVersion(longFormat = false, useBuildNumber = true)) append(versionProvider.getVersion(longFormat = false, useBuildNumber = true))
if (BuildConfig.DEBUG) { if (buildMeta.isDebug) {
append(" ") append(" ")
append(BuildConfig.GIT_BRANCH_NAME) append(buildMeta.gitBranchName)
} }
} }

View File

@ -35,7 +35,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R import im.vector.app.R
import im.vector.app.config.analyticsConfig
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.dialogs.ExportKeysDialog
import im.vector.app.core.extensions.queryExportKeys import im.vector.app.core.extensions.queryExportKeys
@ -51,6 +50,7 @@ import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.openFileSelection import im.vector.app.core.utils.openFileSelection
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.DialogImportE2eKeysBinding import im.vector.app.databinding.DialogImportE2eKeysBinding
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewActions import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewActions
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel
@ -84,7 +84,8 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
private val keysExporter: KeysExporter, private val keysExporter: KeysExporter,
private val keysImporter: KeysImporter, private val keysImporter: KeysImporter,
private val rawService: RawService, private val rawService: RawService,
private val navigator: Navigator private val navigator: Navigator,
private val analyticsConfig: AnalyticsConfig,
) : VectorSettingsBaseFragment() { ) : VectorSettingsBaseFragment() {
override var titleRes = R.string.settings_security_and_privacy override var titleRes = R.string.settings_security_and_privacy

View File

@ -34,7 +34,7 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentSettingsNotificationsTroubleshootBinding import im.vector.app.databinding.FragmentSettingsNotificationsTroubleshootBinding
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationActionIds
import im.vector.app.features.rageshake.BugReporter import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
@ -46,7 +46,8 @@ import javax.inject.Inject
class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
private val bugReporter: BugReporter, private val bugReporter: BugReporter,
private val testManagerFactory: NotificationTroubleshootTestManagerFactory private val testManagerFactory: NotificationTroubleshootTestManagerFactory,
private val actionIds: NotificationActionIds,
) : VectorBaseFragment<FragmentSettingsNotificationsTroubleshootBinding>() { ) : VectorBaseFragment<FragmentSettingsNotificationsTroubleshootBinding>() {
private var testManager: NotificationTroubleshootTestManager? = null private var testManager: NotificationTroubleshootTestManager? = null
@ -151,11 +152,11 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
tryOrNull("Unable to register the receiver") { tryOrNull("Unable to register the receiver") {
LocalBroadcastManager.getInstance(requireContext()) LocalBroadcastManager.getInstance(requireContext())
.registerReceiver(broadcastReceiverPush, IntentFilter(NotificationUtils.PUSH_ACTION)) .registerReceiver(broadcastReceiverPush, IntentFilter(actionIds.push))
} }
tryOrNull("Unable to register the receiver") { tryOrNull("Unable to register the receiver") {
LocalBroadcastManager.getInstance(requireContext()) LocalBroadcastManager.getInstance(requireContext())
.registerReceiver(broadcastReceiverNotification, IntentFilter(NotificationUtils.DIAGNOSTIC_ACTION)) .registerReceiver(broadcastReceiverNotification, IntentFilter(actionIds.diagnostic))
} }
} }

View File

@ -16,24 +16,27 @@
package im.vector.app.features.version package im.vector.app.features.version
import im.vector.app.BuildConfig import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.resources.VersionCodeProvider import im.vector.app.core.resources.VersionCodeProvider
import javax.inject.Inject import javax.inject.Inject
class VersionProvider @Inject constructor(private val versionCodeProvider: VersionCodeProvider) { class VersionProvider @Inject constructor(
private val versionCodeProvider: VersionCodeProvider,
private val buildMeta: BuildMeta,
) {
fun getVersion(longFormat: Boolean, useBuildNumber: Boolean): String { fun getVersion(longFormat: Boolean, useBuildNumber: Boolean): String {
var result = "${BuildConfig.VERSION_NAME} [${versionCodeProvider.getVersionCode()}]" var result = "${buildMeta.versionName} [${versionCodeProvider.getVersionCode()}]"
var flavor = BuildConfig.SHORT_FLAVOR_DESCRIPTION var flavor = buildMeta.flavorShortDescription
if (flavor.isNotBlank()) { if (flavor.isNotBlank()) {
flavor += "-" flavor += "-"
} }
var gitVersion = BuildConfig.GIT_REVISION var gitVersion = buildMeta.gitRevision
val gitRevisionDate = BuildConfig.GIT_REVISION_DATE val gitRevisionDate = buildMeta.gitRevisionDate
val buildNumber = BuildConfig.BUILD_NUMBER val buildNumber = buildMeta.buildNumber
var useLongFormat = longFormat var useLongFormat = longFormat

View File

@ -23,6 +23,7 @@ import im.vector.app.features.session.coroutineScope
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeContext import im.vector.app.test.fakes.FakeContext
import im.vector.app.test.fakes.FakeLocationManager import im.vector.app.test.fakes.FakeLocationManager
import im.vector.app.test.fixtures.aBuildMeta
import im.vector.app.test.test import im.vector.app.test.test
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
@ -56,7 +57,7 @@ class LocationTrackerTest {
@Before @Before
fun setUp() { fun setUp() {
mockkStatic("im.vector.app.features.session.SessionCoroutineScopesKt") mockkStatic("im.vector.app.features.session.SessionCoroutineScopesKt")
locationTracker = LocationTracker(fakeContext.instance, fakeActiveSessionHolder.instance) locationTracker = LocationTracker(fakeContext.instance, fakeActiveSessionHolder.instance, aBuildMeta())
fakeLocationManager.givenRemoveUpdates(locationTracker) fakeLocationManager.givenRemoveUpdates(locationTracker)
} }

View File

@ -17,6 +17,7 @@
package im.vector.app.features.onboarding package im.vector.app.features.onboarding
import android.net.Uri import android.net.Uri
import android.os.Build
import com.airbnb.mvrx.test.MvRxTestRule import com.airbnb.mvrx.test.MvRxTestRule
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.login.LoginConfig import im.vector.app.features.login.LoginConfig
@ -24,6 +25,7 @@ import im.vector.app.features.login.LoginMode
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.login.SignMode import im.vector.app.features.login.SignMode
import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
import im.vector.app.test.TestBuildVersionSdkIntProvider
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeAnalyticsTracker import im.vector.app.test.fakes.FakeAnalyticsTracker
import im.vector.app.test.fakes.FakeAuthenticationService import im.vector.app.test.fakes.FakeAuthenticationService
@ -43,7 +45,6 @@ import im.vector.app.test.fakes.FakeVectorFeatures
import im.vector.app.test.fakes.FakeVectorOverrides import im.vector.app.test.fakes.FakeVectorOverrides
import im.vector.app.test.fakes.toTestString import im.vector.app.test.fakes.toTestString
import im.vector.app.test.fixtures.a401ServerError import im.vector.app.test.fixtures.a401ServerError
import im.vector.app.test.fixtures.aBuildMeta
import im.vector.app.test.fixtures.aHomeServerCapabilities import im.vector.app.test.fixtures.aHomeServerCapabilities
import im.vector.app.test.test import im.vector.app.test.test
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -720,7 +721,7 @@ class OnboardingViewModelTest {
fakeStartAuthenticationFlowUseCase.instance, fakeStartAuthenticationFlowUseCase.instance,
FakeVectorOverrides(), FakeVectorOverrides(),
fakeRegistrationActionHandler.instance, fakeRegistrationActionHandler.instance,
aBuildMeta(), TestBuildVersionSdkIntProvider().also { it.value = Build.VERSION_CODES.O },
).also { ).also {
viewModel = it viewModel = it
initialState = state initialState = state

View File

@ -24,10 +24,5 @@ object AnalyticsConfigFixture {
postHogHost: String = "http://posthog.url", postHogHost: String = "http://posthog.url",
postHogApiKey: String = "api-key", postHogApiKey: String = "api-key",
policyLink: String = "http://policy.link" policyLink: String = "http://policy.link"
) = object : AnalyticsConfig { ) = AnalyticsConfig(isEnabled, postHogHost, postHogApiKey, policyLink)
override val isEnabled: Boolean = isEnabled
override val postHogHost = postHogHost
override val postHogApiKey = postHogApiKey
override val policyLink = policyLink
}
} }

View File

@ -16,7 +16,17 @@
package im.vector.app.test.fixtures package im.vector.app.test.fixtures
import android.os.Build
import im.vector.app.core.resources.BuildMeta import im.vector.app.core.resources.BuildMeta
fun aBuildMeta() = BuildMeta(Build.VERSION_CODES.O) fun aBuildMeta() = BuildMeta(
isDebug = false,
applicationId = "im.vector",
lowPrivacyLoggingEnabled = false,
versionName = "app-version-name",
gitRevision = "abcdef",
gitRevisionDate = "01-01-01",
gitBranchName = "a-branch-name",
buildNumber = "100",
flavorDescription = "Gplay",
flavorShortDescription = "",
)