diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 59991cdcf9..92380492a2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,8 +16,6 @@ jobs:
debug:
name: Build APKs
runs-on: ubuntu-latest
- # Skip for `main`
- if: github.ref != 'refs/heads/main'
strategy:
matrix:
variant: [debug, release, nightly, samples]
diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml
new file mode 100644
index 0000000000..7547d5291c
--- /dev/null
+++ b/.github/workflows/build_enterprise.yml
@@ -0,0 +1,69 @@
+name: Enterprise APK Build
+
+on:
+ workflow_dispatch:
+ pull_request:
+ merge_group:
+ push:
+ branches: [ develop ]
+
+# Enrich gradle.properties for CI/CD
+env:
+ GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC
+ CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true
+
+jobs:
+ build:
+ name: Build Enterprise APKs
+ runs-on: ubuntu-latest
+ # Skip in forks
+ if: github.repository == 'element-hq/element-x-android'
+ strategy:
+ matrix:
+ variant: [debug, release, nightly]
+ fail-fast: false
+ # Allow all jobs on develop. Just one per PR.
+ concurrency:
+ group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-enterprise-{0}-{1}', matrix.variant, github.sha) || format('build-enterprise-{0}-{1}', matrix.variant, github.ref) }}
+ cancel-in-progress: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ # Ensure we are building the branch and not the branch after being merged on develop
+ # https://github.com/actions/checkout/issues/881
+ ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ run: git submodule update --init --recursive
+ - name: Use JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin' # See 'Supported distributions' for available options
+ java-version: '17'
+ - name: Configure gradle
+ uses: gradle/actions/setup-gradle@v3
+ with:
+ cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
+ - name: Assemble debug Gplay Enterprise APK
+ if: ${{ matrix.variant == 'debug' }}
+ env:
+ ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
+ run: ./gradlew :app:assembleGplayDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
+ - name: Upload debug Enterprise APKs
+ if: ${{ matrix.variant == 'debug' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: elementx-enterprise-debug
+ path: |
+ app/build/outputs/apk/gplay/debug/*-universal-debug.apk
+ - name: Compile release sources
+ if: ${{ matrix.variant == 'release' }}
+ run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
+ - name: Compile nightly sources
+ if: ${{ matrix.variant == 'nightly' }}
+ run: ./gradlew compileGplayNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index 4d71f56617..b4fd79e5dd 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -8,6 +8,13 @@ jobs:
name: Danger main check
steps:
- uses: actions/checkout@v4
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
diff --git a/.github/workflows/nightly_enterprise.yml b/.github/workflows/nightly_enterprise.yml
new file mode 100644
index 0000000000..9cd80fe094
--- /dev/null
+++ b/.github/workflows/nightly_enterprise.yml
@@ -0,0 +1,57 @@
+name: Build and release Enterprise nightly application
+
+on:
+ workflow_dispatch:
+ schedule:
+ # Every nights at 4
+ - cron: "0 4 * * *"
+
+env:
+ GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC
+ CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true
+
+jobs:
+ nightly:
+ name: Build and publish Enterprise nightly bundle to Firebase
+ runs-on: ubuntu-latest
+ if: ${{ github.repository == 'element-hq/element-x-android' }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ run: git submodule update --init --recursive
+ - name: Use JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin' # See 'Supported distributions' for available options
+ java-version: '17'
+ - name: Install towncrier
+ run: |
+ python3 -m pip install towncrier
+ - name: Prepare changelog file
+ run: |
+ mv towncrier.toml towncrier.toml.bak
+ sed 's/CHANGES\.md/CHANGES_NIGHTLY\.md/' towncrier.toml.bak > towncrier.toml
+ rm towncrier.toml.bak
+ yes n | towncrier build --version nightly
+ - name: Build and upload Nightly application
+ run: |
+ ./gradlew assembleGplayNightly appDistributionUploadGplayNightly $CI_GRADLE_ARG_PROPERTIES
+ env:
+ ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
+ ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }}
+ ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }}
+ ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }}
+ FIREBASE_TOKEN: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_FIREBASE_TOKEN }}
+ - name: Additionally upload Nightly APK to browserstack for testing
+ continue-on-error: true # don't block anything by this upload failing (for now)
+ run: |
+ curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/gplay/nightly/app-gplay-universal-nightly.apk" -F "custom_id=element-x-android-nightly"
+ env:
+ BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }}
+ BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }}
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index 1c3ab1d7d5..ff435ee988 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -18,6 +18,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: Run code quality check suite
run: ./tools/check/check_code_quality.sh
@@ -68,6 +75,13 @@ jobs:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: Use JDK 17
uses: actions/setup-java@v4
with:
@@ -100,6 +114,13 @@ jobs:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: Use JDK 17
uses: actions/setup-java@v4
with:
@@ -136,6 +157,13 @@ jobs:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: Use JDK 17
uses: actions/setup-java@v4
with:
@@ -168,6 +196,13 @@ jobs:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: Use JDK 17
uses: actions/setup-java@v4
with:
@@ -200,6 +235,13 @@ jobs:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: Use JDK 17
uses: actions/setup-java@v4
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f596d5e438..b58f2f3348 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,6 +39,40 @@ jobs:
path: |
app/build/outputs/bundle/gplayRelease/app-gplay-release.aab
+ enterprise:
+ name: Create App Bundle Enterprise
+ runs-on: ubuntu-latest
+ concurrency:
+ group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
+ cancel-in-progress: true
+ steps:
+ - uses: actions/checkout@v4
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ run: git submodule update --init --recursive
+ - name: Use JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin' # See 'Supported distributions' for available options
+ java-version: '17'
+ - name: Configure gradle
+ uses: gradle/actions/setup-gradle@v3
+ - name: Create Enterprise app bundle
+ env:
+ ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
+ run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
+ - name: Upload bundle as artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: elementx-enterprise-app-gplay-bundle-unsigned
+ path: |
+ app/build/outputs/bundle/gplayRelease/app-gplay-release.aab
+
fdroid:
name: Create APKs (FDroid)
runs-on: ubuntu-latest
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 1d41100940..a132fd7d14 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -38,6 +38,13 @@ jobs:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Add SSH private keys for submodule repositories
+ uses: webfactory/ssh-agent@v0.9.0
+ with:
+ ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
+ - name: Clone submodules
+ if: github.repository == 'element-hq/element-x-android'
+ run: git submodule update --init --recursive
- name: ☕️ Use JDK 17
uses: actions/setup-java@v4
with:
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..aa54a9d8ae
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "enterprise"]
+ path = enterprise
+ url = git@github.com:element-hq/element-android-enterprise.git
diff --git a/.idea/copyright/Element_FOSS.xml b/.idea/copyright/Element_FOSS.xml
new file mode 100644
index 0000000000..4ab92252ae
--- /dev/null
+++ b/.idea/copyright/Element_FOSS.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/copyright/Enterprise.xml b/.idea/copyright/Enterprise.xml
new file mode 100644
index 0000000000..2d430b4613
--- /dev/null
+++ b/.idea/copyright/Enterprise.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/copyright/NewVector.xml b/.idea/copyright/NewVector.xml
deleted file mode 100644
index 72a4f2e779..0000000000
--- a/.idea/copyright/NewVector.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
index 0875fcecb1..69d52afca4 100644
--- a/.idea/copyright/profiles_settings.xml
+++ b/.idea/copyright/profiles_settings.xml
@@ -1,3 +1,7 @@
-
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Enterprise.xml b/.idea/scopes/Enterprise.xml
new file mode 100644
index 0000000000..83599ae680
--- /dev/null
+++ b/.idea/scopes/Enterprise.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c2db5acdf1..7bc3504d84 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -17,6 +17,7 @@
@file:Suppress("UnstableApiUsage")
import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
+import extension.allEnterpriseImpl
import extension.allFeaturesImpl
import extension.allLibrariesImpl
import extension.allServicesImpl
@@ -46,7 +47,11 @@ android {
namespace = "io.element.android.x"
defaultConfig {
- applicationId = "io.element.android.x"
+ applicationId = if (isEnterpriseBuild) {
+ "io.element.enterprise"
+ } else {
+ "io.element.android.x"
+ }
targetSdk = Versions.targetSdk
versionCode = Versions.versionCode
versionName = Versions.versionName
@@ -99,15 +104,22 @@ android {
}
}
+ val baseAppName = if (isEnterpriseBuild) {
+ "Element Enterprise"
+ } else {
+ "Element X"
+ }
+ logger.warnInBox("Building $baseAppName")
+
buildTypes {
getByName("debug") {
- resValue("string", "app_name", "Element X dbg")
+ resValue("string", "app_name", "$baseAppName dbg")
applicationIdSuffix = ".debug"
signingConfig = signingConfigs.getByName("debug")
}
getByName("release") {
- resValue("string", "app_name", "Element X")
+ resValue("string", "app_name", baseAppName)
signingConfig = signingConfigs.getByName("debug")
postprocessing {
@@ -124,7 +136,7 @@ android {
initWith(release)
applicationIdSuffix = ".nightly"
versionNameSuffix = "-nightly"
- resValue("string", "app_name", "Element X nightly")
+ resValue("string", "app_name", "$baseAppName nightly")
matchingFallbacks += listOf("release")
signingConfig = signingConfigs.getByName("nightly")
@@ -220,6 +232,9 @@ knit {
dependencies {
allLibrariesImpl()
allServicesImpl()
+ if (isEnterpriseBuild) {
+ allEnterpriseImpl(rootDir, logger)
+ }
allFeaturesImpl(rootDir, logger)
implementation(projects.features.migration.api)
implementation(projects.anvilannotations)
diff --git a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt
index dd0ad775fe..7614d54e01 100644
--- a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt
+++ b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt
@@ -24,6 +24,7 @@ import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import io.element.android.appconfig.ApplicationConfig
+import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.messages.impl.timeline.components.customreaction.DefaultEmojibaseProvider
import io.element.android.features.messages.impl.timeline.components.customreaction.EmojibaseProvider
import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest
@@ -77,13 +78,18 @@ object AppModule {
@Provides
@SingleIn(AppScope::class)
- fun providesBuildMeta(@ApplicationContext context: Context, buildType: BuildType) = BuildMeta(
+ fun providesBuildMeta(
+ @ApplicationContext context: Context,
+ buildType: BuildType,
+ enterpriseService: EnterpriseService,
+ ) = BuildMeta(
isDebuggable = BuildConfig.DEBUG,
buildType = buildType,
applicationName = ApplicationConfig.APPLICATION_NAME.takeIf { it.isNotEmpty() } ?: context.getString(R.string.app_name),
productionApplicationName = ApplicationConfig.PRODUCTION_APPLICATION_NAME,
desktopApplicationName = ApplicationConfig.DESKTOP_APPLICATION_NAME,
applicationId = BuildConfig.APPLICATION_ID,
+ isEnterpriseBuild = enterpriseService.isEnterpriseBuild,
// TODO EAx Config.LOW_PRIVACY_LOG_ENABLE,
lowPrivacyLoggingEnabled = false,
versionName = BuildConfig.VERSION_NAME,
diff --git a/enterprise b/enterprise
new file mode 160000
index 0000000000..ceb65e32d9
--- /dev/null
+++ b/enterprise
@@ -0,0 +1 @@
+Subproject commit ceb65e32d95052c028a37654a4a0410639f69053
diff --git a/features/enterprise/api/build.gradle.kts b/features/enterprise/api/build.gradle.kts
new file mode 100644
index 0000000000..6e9cd26834
--- /dev/null
+++ b/features/enterprise/api/build.gradle.kts
@@ -0,0 +1,27 @@
+/*
+ * 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.enterprise.api"
+}
+
+dependencies {
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+}
diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt
new file mode 100644
index 0000000000..3c27af8629
--- /dev/null
+++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.enterprise.api
+
+import io.element.android.libraries.matrix.api.core.SessionId
+
+interface EnterpriseService {
+ val isEnterpriseBuild: Boolean
+ suspend fun isEnterpriseUser(sessionId: SessionId): Boolean
+}
diff --git a/features/enterprise/impl/build.gradle.kts b/features/enterprise/impl/build.gradle.kts
new file mode 100644
index 0000000000..eb87d9310b
--- /dev/null
+++ b/features/enterprise/impl/build.gradle.kts
@@ -0,0 +1,35 @@
+/*
+ * 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")
+ alias(libs.plugins.anvil)
+}
+
+android {
+ namespace = "io.element.android.features.enterprise.impl"
+}
+
+dependencies {
+ implementation(projects.anvilannotations)
+ api(projects.features.enterprise.api)
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+
+ testImplementation(libs.coroutines.test)
+ testImplementation(libs.test.junit)
+ testImplementation(libs.test.truth)
+ testImplementation(projects.libraries.matrix.test)
+}
diff --git a/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt
new file mode 100644
index 0000000000..c3387259b8
--- /dev/null
+++ b/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.enterprise.impl
+
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.enterprise.api.EnterpriseService
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.matrix.api.core.SessionId
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class DefaultEnterpriseService @Inject constructor() : EnterpriseService {
+ override val isEnterpriseBuild = false
+
+ override suspend fun isEnterpriseUser(sessionId: SessionId) = false
+}
diff --git a/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt b/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt
new file mode 100644
index 0000000000..003ca2ffc9
--- /dev/null
+++ b/features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.enterprise.impl
+
+import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.matrix.test.A_SESSION_ID
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class DefaultEnterpriseServiceTest {
+ @Test
+ fun `isEnterpriseBuild is false`() {
+ val defaultEnterpriseService = DefaultEnterpriseService()
+ assertThat(defaultEnterpriseService.isEnterpriseBuild).isFalse()
+ }
+
+ @Test
+ fun `isEnterpriseUser always return false`() = runTest {
+ val defaultEnterpriseService = DefaultEnterpriseService()
+ assertThat(defaultEnterpriseService.isEnterpriseUser(A_SESSION_ID)).isFalse()
+ }
+}
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
index f9f8d7e5b9..72e668c3f4 100755
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
@@ -169,6 +169,9 @@ class DefaultBugReporter @Inject constructor(
currentTracingFilter?.let {
builder.addFormDataPart("tracing_filter", it)
}
+ if (buildMeta.isEnterpriseBuild) {
+ builder.addFormDataPart("label", "Enterprise")
+ }
// add the gzipped files, don't cancel the whole upload if only some file failed to upload
var totalUploadedSize = 0L
var uploadedSomeLogs = false
diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
index 49e055cc29..5b9866254d 100644
--- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
@@ -26,16 +26,12 @@ import android.os.Build
import android.provider.Settings
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
-import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.RequiresApi
import androidx.core.content.pm.PackageInfoCompat
import io.element.android.libraries.androidutils.R
import io.element.android.libraries.androidutils.compat.getApplicationInfoCompat
import io.element.android.libraries.core.mimetype.MimeTypes
-@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
-fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-
/**
* Return the application label of the provided package. If not found, the package is returned.
*/
diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/meta/BuildMeta.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/meta/BuildMeta.kt
index a9cb78548f..2d62a5be70 100644
--- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/meta/BuildMeta.kt
+++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/meta/BuildMeta.kt
@@ -23,6 +23,7 @@ data class BuildMeta(
val productionApplicationName: String,
val desktopApplicationName: String,
val applicationId: String,
+ val isEnterpriseBuild: Boolean,
val lowPrivacyLoggingEnabled: Boolean,
val versionName: String,
val versionCode: Long,
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/core/BuildMeta.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/core/BuildMeta.kt
index 52c15d05a4..569c33db7d 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/core/BuildMeta.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/core/BuildMeta.kt
@@ -26,6 +26,7 @@ fun aBuildMeta(
productionApplicationName: String = applicationName,
desktopApplicationName: String = applicationName,
applicationId: String = "",
+ isEnterpriseBuild: Boolean = false,
lowPrivacyLoggingEnabled: Boolean = true,
versionName: String = "",
versionCode: Long = 0,
@@ -40,6 +41,7 @@ fun aBuildMeta(
productionApplicationName = productionApplicationName,
desktopApplicationName = desktopApplicationName,
applicationId = applicationId,
+ isEnterpriseBuild = isEnterpriseBuild,
lowPrivacyLoggingEnabled = lowPrivacyLoggingEnabled,
versionName = versionName,
versionCode = versionCode,
diff --git a/plugins/src/main/kotlin/Enterprise.kt b/plugins/src/main/kotlin/Enterprise.kt
new file mode 100644
index 0000000000..65762c05d6
--- /dev/null
+++ b/plugins/src/main/kotlin/Enterprise.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+import java.io.File
+
+/**
+ * Are we building with the enterprise sources?
+ */
+val isEnterpriseBuild = File("enterprise/README.md").exists()
diff --git a/plugins/src/main/kotlin/Logger.kt b/plugins/src/main/kotlin/Logger.kt
new file mode 100644
index 0000000000..fa608e7df4
--- /dev/null
+++ b/plugins/src/main/kotlin/Logger.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+import org.gradle.api.logging.Logger
+import kotlin.math.max
+
+fun Logger.warnInBox(
+ text: String,
+ minBoxWidth: Int = 80,
+ padding: Int = 4,
+) {
+ val textLength = text.length
+ val boxWidth = max(textLength + 2, minBoxWidth)
+ val textPadding = max((boxWidth - textLength) / 2, 1)
+ warn(
+ buildString {
+ append(" ".repeat(padding))
+ append("┌")
+ append("─".repeat(boxWidth))
+ append("┐")
+ }
+ )
+ warn(
+ buildString {
+ append(" ".repeat(padding))
+ append("│")
+ append(" ".repeat(textPadding))
+ append(text)
+ append(" ".repeat(textPadding))
+ if (textLength % 2 == 1 && boxWidth == minBoxWidth) append(" ")
+ append("│")
+ }
+ )
+ warn(
+ buildString {
+ append(" ".repeat(padding))
+ append("└")
+ append("─".repeat(boxWidth))
+ append("┘")
+ }
+ )
+}
diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt
index 76a66dd519..97ac5aeaf5 100644
--- a/plugins/src/main/kotlin/Versions.kt
+++ b/plugins/src/main/kotlin/Versions.kt
@@ -64,7 +64,7 @@ object Versions {
const val compileSdk = 34
const val targetSdk = 33
// When updating the `minSdk`, make sure to update the value of `minSdkVersion` in the file `tools/release/release.sh`
- const val minSdk = 24
+ val minSdk = if (isEnterpriseBuild) 26 else 24
val javaCompileVersion = JavaVersion.VERSION_17
val javaLanguageVersion: JavaLanguageVersion = JavaLanguageVersion.of(11)
}
diff --git a/plugins/src/main/kotlin/extension/CommonExtension.kt b/plugins/src/main/kotlin/extension/CommonExtension.kt
index 271b1565f5..bc063d5921 100644
--- a/plugins/src/main/kotlin/extension/CommonExtension.kt
+++ b/plugins/src/main/kotlin/extension/CommonExtension.kt
@@ -18,6 +18,7 @@ package extension
import Versions
import com.android.build.api.dsl.CommonExtension
+import isEnterpriseBuild
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.JavaVersion
import org.gradle.api.Project
@@ -46,6 +47,11 @@ fun CommonExtension<*, *, *, *, *, *>.androidConfig(project: Project) {
lint {
lintConfig = File("${project.rootDir}/tools/lint/lint.xml")
+ if (isEnterpriseBuild) {
+ // Disable check on ObsoleteSdkInt for Enterprise builds
+ // since the min sdk is higher for Enterprise builds
+ disable.add("ObsoleteSdkInt")
+ }
checkDependencies = false
abortOnError = true
ignoreTestFixturesSources = true
diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
index 4aa635b489..9fd82af4ae 100644
--- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
+++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
@@ -130,6 +130,11 @@ fun DependencyHandlerScope.allServicesImpl() {
implementation(project(":services:toolbox:impl"))
}
+fun DependencyHandlerScope.allEnterpriseImpl(rootDir: File, logger: Logger) {
+ val enterpriseDir = File(rootDir, "enterprise")
+ addImplementationProjects(enterpriseDir, ":enterprise", "impl", logger)
+}
+
fun DependencyHandlerScope.allFeaturesApi(rootDir: File, logger: Logger) {
val featuresDir = File(rootDir, "features")
addImplementationProjects(featuresDir, ":features", "api", logger)
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt
index 9c09d0bc98..22cfd643a1 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt
@@ -36,6 +36,7 @@ object Singleton {
productionApplicationName = "EAX-Minimal",
desktopApplicationName = "EAX-Minimal-Desktop",
applicationId = "io.element.android.samples.minimal",
+ isEnterpriseBuild = false,
lowPrivacyLoggingEnabled = false,
versionName = "0.1.0",
versionCode = 1,
diff --git a/settings.gradle.kts b/settings.gradle.kts
index b154f4585e..693a258447 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -87,6 +87,7 @@ fun includeProjects(directory: File, path: String, maxDepth: Int = 1) {
}
}
+includeProjects(File(rootDir, "enterprise"), ":enterprise", maxDepth = 2)
includeProjects(File(rootDir, "features"), ":features")
includeProjects(File(rootDir, "libraries"), ":libraries")
includeProjects(File(rootDir, "services"), ":services")
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt
index f126640149..a34f0282a7 100644
--- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt
@@ -112,6 +112,7 @@ class KonsistClassNameTest {
"DBov",
"Default",
"DataStore",
+ "Enterprise",
"FileExtensionExtractor",
"KeyStore",
"Matrix",
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt
new file mode 100644
index 0000000000..e6cbf9ca14
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistLicenseTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.tests.konsist
+
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.verify.assertTrue
+import org.junit.Test
+
+class KonsistLicenseTest {
+ private val publicLicense = """
+ /\*
+ (?:.*\n)* \* Copyright \(c\) 20\d\d New Vector Ltd
+ (?:.*\n)* \*
+ \* 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
+ \*
+ \* {5}https?://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\.
+ \*/
+ """.trimIndent().toRegex()
+
+ private val enterpriseLicense = """
+ /\*
+ \* © 20\d\d New Vector Limited, Element Software SARL, Element Software Inc\.,
+ \* and Element Software GmbH \(the "Element Group"\) only make this file available
+ \* under a proprietary license model\.
+ \*
+ \* Without a proprietary license with us, you cannot use this file\. The terms of
+ \* the proprietary license agreement between you and any member of the Element Group
+ \* shall always apply to your use of this file\. Unauthorised use, copying, distribution,
+ \* or modification of this file, via any medium, is strictly prohibited\.
+ \*
+ \* For details about the licensing terms, you must either visit our website or contact
+ \* a member of our sales team\.
+ \*/
+ """.trimIndent().toRegex()
+
+ @Test
+ fun `assert that FOSS files have the correct license header`() {
+ Konsist
+ .scopeFromProject()
+ .files
+ .filter {
+ it.path.contains("/enterprise/features").not() &&
+ it.nameWithExtension != "locales.kt" &&
+ it.name.startsWith("Template ").not()
+ }
+ .assertTrue {
+ publicLicense.containsMatchIn(it.text)
+ }
+ }
+
+ @Test
+ fun `assert that Enterprise files have the correct license header`() {
+ Konsist
+ .scopeFromProject()
+ .files
+ .filter {
+ it.path.contains("/enterprise/features")
+ }
+ .assertTrue {
+ enterpriseLicense.containsMatchIn(it.text)
+ }
+ }
+}
diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml
index cffb824ea4..7212a58ce3 100644
--- a/tools/detekt/detekt.yml
+++ b/tools/detekt/detekt.yml
@@ -197,9 +197,7 @@ performance:
# Note: all rules for `comments` are disabled by default, but I put them here to be aware of their existence
comments:
AbsentOrWrongFileLicense:
- active: true
- licenseTemplateFile: 'license.template'
- licenseTemplateIsRegex: true
+ active: false
CommentOverPrivateFunction:
active: false
CommentOverPrivateProperty:
diff --git a/tools/detekt/license.template b/tools/detekt/license.template
deleted file mode 100644
index 08cadc82f9..0000000000
--- a/tools/detekt/license.template
+++ /dev/null
@@ -1,15 +0,0 @@
-\/\*
-(?:.*\n)* \* Copyright \(c\) 20\d\d New Vector Ltd
-(?:.*\n)* \*
- \* 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(?:s)?:\/\/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\.
- \*\/