diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8c9a247172..b3872a18fd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -120,10 +120,18 @@
+
+
+
+
-
+
@@ -136,7 +144,7 @@
-
+
{
@Composable
@@ -52,6 +54,10 @@ class RootPresenter @Inject constructor(
)
}
+ LaunchedEffect(Unit) {
+ shareService.observeFeatureFlag(this)
+ }
+
return RootState(
rageshakeDetectionState = rageshakeDetectionState,
crashDetectionState = crashDetectionState,
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
index 118ecd1e46..aeaeb10859 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
@@ -28,6 +28,8 @@ import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
+import io.element.android.features.share.api.ShareService
+import io.element.android.features.share.test.FakeShareService
import io.element.android.libraries.matrix.test.FakeSdkMetadata
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.services.analytics.test.FakeAnalyticsService
@@ -35,6 +37,8 @@ import io.element.android.services.apperror.api.AppErrorState
import io.element.android.services.apperror.api.AppErrorStateService
import io.element.android.services.apperror.impl.DefaultAppErrorStateService
import io.element.android.tests.testutils.WarmUpRule
+import io.element.android.tests.testutils.lambda.lambdaRecorder
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@@ -55,6 +59,22 @@ class RootPresenterTest {
}
}
+ @Test
+ fun `present - check that share service is invoked`() = runTest {
+ val lambda = lambdaRecorder { _ -> }
+ val presenter = createRootPresenter(
+ shareService = FakeShareService {
+ lambda(it)
+ }
+ )
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(2)
+ lambda.assertions().isCalledOnce()
+ }
+ }
+
@Test
fun `present - passes app error state`() = runTest {
val presenter = createRootPresenter(
@@ -79,7 +99,8 @@ class RootPresenterTest {
}
private fun createRootPresenter(
- appErrorService: AppErrorStateService = DefaultAppErrorStateService()
+ appErrorService: AppErrorStateService = DefaultAppErrorStateService(),
+ shareService: ShareService = FakeShareService {},
): RootPresenter {
val crashDataStore = FakeCrashDataStore()
val rageshakeDataStore = FakeRageshakeDataStore()
@@ -102,6 +123,7 @@ class RootPresenterTest {
rageshakeDetectionPresenter = rageshakeDetectionPresenter,
appErrorStateService = appErrorService,
analyticsService = FakeAnalyticsService(),
+ shareService = shareService,
sdkMetadata = FakeSdkMetadata("sha")
)
}
diff --git a/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareService.kt b/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareService.kt
new file mode 100644
index 0000000000..c46f5b3215
--- /dev/null
+++ b/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareService.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2024 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 io.element.android.features.share.api
+
+import kotlinx.coroutines.CoroutineScope
+
+interface ShareService {
+ fun observeFeatureFlag(coroutineScope: CoroutineScope)
+}
diff --git a/features/share/impl/build.gradle.kts b/features/share/impl/build.gradle.kts
index bc9fdee42d..c180c5d29a 100644
--- a/features/share/impl/build.gradle.kts
+++ b/features/share/impl/build.gradle.kts
@@ -43,6 +43,7 @@ dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
+ implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt
new file mode 100644
index 0000000000..89a94c2a95
--- /dev/null
+++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareService.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2024 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 io.element.android.features.share.impl
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.share.api.ShareService
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.ApplicationContext
+import io.element.android.libraries.featureflag.api.FeatureFlagService
+import io.element.android.libraries.featureflag.api.FeatureFlags
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class DefaultShareService @Inject constructor(
+ private val featureFlagService: FeatureFlagService,
+ @ApplicationContext private val context: Context,
+) : ShareService {
+ override fun observeFeatureFlag(coroutineScope: CoroutineScope) {
+ val shareActivityComponent = context
+ .packageManager
+ .getPackageInfo(
+ context.packageName,
+ PackageManager.GET_ACTIVITIES or PackageManager.MATCH_DISABLED_COMPONENTS
+ )
+ .activities
+ .firstOrNull { it.name.endsWith(".ShareActivity") }
+ ?.let { shareActivityInfo ->
+ ComponentName(
+ shareActivityInfo.packageName,
+ shareActivityInfo.name,
+ )
+ }
+ ?: return Unit.also {
+ Timber.w("ShareActivity not found")
+ }
+ featureFlagService.isFeatureEnabledFlow(FeatureFlags.IncomingShare)
+ .onEach { enabled ->
+ val state = if (enabled) {
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ } else {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ }
+ try {
+ context.packageManager.setComponentEnabledSetting(
+ shareActivityComponent,
+ state,
+ PackageManager.DONT_KILL_APP,
+ )
+ } catch (e: Exception) {
+ Timber.e(e, "Failed to enable or disable the component")
+ }
+ }
+ .launchIn(coroutineScope)
+ }
+}
diff --git a/features/share/test/build.gradle.kts b/features/share/test/build.gradle.kts
new file mode 100644
index 0000000000..0eaa0bedd2
--- /dev/null
+++ b/features/share/test/build.gradle.kts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.features.share.test"
+}
+
+dependencies {
+ implementation(projects.features.share.api)
+ implementation(libs.coroutines.core)
+ implementation(projects.tests.testutils)
+}
diff --git a/features/share/test/src/main/kotlin/io/element/android/features/share/test/FakeShareService.kt b/features/share/test/src/main/kotlin/io/element/android/features/share/test/FakeShareService.kt
new file mode 100644
index 0000000000..302d8e2a54
--- /dev/null
+++ b/features/share/test/src/main/kotlin/io/element/android/features/share/test/FakeShareService.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2024 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 io.element.android.features.share.test
+
+import io.element.android.features.share.api.ShareService
+import io.element.android.tests.testutils.lambda.lambdaError
+import kotlinx.coroutines.CoroutineScope
+
+class FakeShareService(
+ private val observeFeatureFlagLambda: (CoroutineScope) -> Unit = { lambdaError() }
+) : ShareService {
+ override fun observeFeatureFlag(coroutineScope: CoroutineScope) {
+ observeFeatureFlagLambda(coroutineScope)
+ }
+}
diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
index 5c3bd8efd4..4d0c50a533 100644
--- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
+++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
@@ -96,4 +96,11 @@ enum class FeatureFlags(
defaultValue = true,
isFinished = false,
),
+ IncomingShare(
+ key = "feature.incomingShare",
+ title = "Incoming Share support",
+ description = "Allow the application to receive data from other applications",
+ defaultValue = true,
+ isFinished = false,
+ ),
}
diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
index 139877500b..7574144066 100644
--- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
+++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
@@ -44,6 +44,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
FeatureFlags.RoomDirectorySearch -> false
FeatureFlags.ShowBlockedUsersDetails -> false
FeatureFlags.QrCodeLogin -> OnBoardingConfig.CAN_LOGIN_WITH_QR_CODE
+ FeatureFlags.IncomingShare -> true
}
} else {
false