diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 859e26defc..15092ad82c 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -33,6 +33,27 @@ jobs: - name: Search for invalid screenshot files run: ./tools/test/checkInvalidScreenshots.py + checkDependencies: + name: Search for invalid dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - 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: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Search for invalid dependencies + run: ./tools/dependencies/checkDependencies.py + # Code checks konsist: name: Konsist tests diff --git a/.idea/dictionaries/shared.xml b/.idea/dictionaries/shared.xml index 01db2cef83..e7bc929689 100644 --- a/.idea/dictionaries/shared.xml +++ b/.idea/dictionaries/shared.xml @@ -13,6 +13,7 @@ onboarding placeables posthog + rageshake securebackup showkase snackbar diff --git a/README.md b/README.md index 7924c369a5..35d4cffb27 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ The application is a total rewrite of [Element-Android](https://github.com/eleme Learn more about why we are building Element X in our blog post: [https://element.io/blog/element-x-experience-the-future-of-element/](https://element.io/blog/element-x-experience-the-future-of-element/). +[Get it on Google Play](https://play.google.com/store/apps/details?id=io.element.android.x)[Get it on F-Droid](https://f-droid.org/packages/io.element.android.x) + ## Table of contents @@ -25,13 +27,13 @@ Learn more about why we are building Element X in our blog post: [https://elemen * [Contributing](#contributing) * [Build instructions](#build-instructions) * [Support](#support) -* [Copyright & License](#copyright-&-license) +* [Copyright and License](#copyright-and-license) ## Screenshots -Here are some early screenshots of the application: +Here are some screenshots of the application: -||||| +||||| |-|-|-|-| -||||| +||||| ## Translations @@ -90,7 +92,7 @@ When you are experiencing an issue on Element X Android, please first search in and then in [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting from the application settings. This is especially recommended when you encounter a crash. -## Copyright & License +## Copyright and License Copyright © New Vector Ltd diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4c21cef589..a4a4b1ee16 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -223,7 +223,6 @@ dependencies { allLibrariesImpl() allServicesImpl() allFeaturesImpl(rootDir, logger) - implementation(projects.features.call) implementation(projects.features.migration.api) implementation(projects.anvilannotations) implementation(projects.appnav) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml deleted file mode 100644 index fee1385c85..0000000000 --- a/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - ignored - diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/ApplicationConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/ApplicationConfig.kt index e8a8a2b33a..21af158ad6 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/ApplicationConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/ApplicationConfig.kt @@ -40,9 +40,4 @@ object ApplicationConfig { * For Element, the value is "Element". We use the same name for desktop and mobile for now. */ const val DESKTOP_APPLICATION_NAME: String = "Element" - - /** - * The maximum size of the upload request. Default value is just below CloudFlare's max request size. - */ - const val MAX_LOG_UPLOAD_SIZE = 50 * 1024 * 1024L } diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.kt new file mode 100644 index 0000000000..78d55e496e --- /dev/null +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/RageshakeConfig.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.appconfig + +object RageshakeConfig { + /** + * The URL to submit bug reports to. + */ + const val BUG_REPORT_URL = "https://riot.im/bugreports/submit" + + /** + * As per https://github.com/matrix-org/rageshake: + * Identifier for the application (eg 'riot-web'). + * Should correspond to a mapping configured in the configuration file for github issue reporting to work. + */ + const val BUG_REPORT_APP_NAME = "element-x-android" + + /** + * The maximum size of the upload request. Default value is just below CloudFlare's max request size. + */ + const val MAX_LOG_UPLOAD_SIZE = 50 * 1024 * 1024L +} diff --git a/features/ftue/impl/build.gradle.kts b/features/ftue/impl/build.gradle.kts index e42763d97c..4d84dc8fa3 100644 --- a/features/ftue/impl/build.gradle.kts +++ b/features/ftue/impl/build.gradle.kts @@ -50,7 +50,6 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.permissions.noop) implementation(projects.services.toolbox.api) - implementation(projects.services.toolbox.test) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) @@ -63,6 +62,7 @@ dependencies { testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.features.lockscreen.test) + testImplementation(projects.services.toolbox.test) testImplementation(projects.tests.testutils) ksp(libs.showkase.processor) diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index 168e72ba3d..030955ed72 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -58,6 +58,6 @@ dependencies { testImplementation(projects.libraries.cryptography.test) testImplementation(projects.libraries.cryptography.impl) testImplementation(projects.libraries.featureflag.test) - implementation(projects.libraries.sessionStorage.test) - implementation(projects.services.appnavstate.test) + testImplementation(projects.libraries.sessionStorage.test) + testImplementation(projects.services.appnavstate.test) } diff --git a/features/logout/impl/src/main/AndroidManifest.xml b/features/logout/impl/src/main/AndroidManifest.xml deleted file mode 100644 index 86d497f107..0000000000 --- a/features/logout/impl/src/main/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/features/logout/impl/src/main/res/values/tmp.xml b/features/logout/impl/src/main/res/values/tmp.xml deleted file mode 100644 index e9e1e376c6..0000000000 --- a/features/logout/impl/src/main/res/values/tmp.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - 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 b4e8a2effb..9e89ff9a76 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 @@ -21,12 +21,11 @@ import android.os.Build import androidx.core.net.toFile import androidx.core.net.toUri import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.appconfig.ApplicationConfig +import io.element.android.appconfig.RageshakeConfig import io.element.android.features.rageshake.api.crash.CrashDataStore import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporterListener import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder -import io.element.android.features.rageshake.impl.R import io.element.android.libraries.androidutils.file.compressFile import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -139,7 +138,7 @@ class DefaultBugReporter @Inject constructor( // build the multi part request val builder = BugReporterMultipartBody.Builder() .addFormDataPart("text", bugDescription) - .addFormDataPart("app", context.getString(R.string.bug_report_app_name)) + .addFormDataPart("app", RageshakeConfig.BUG_REPORT_APP_NAME) .addFormDataPart("user_agent", userAgentProvider.provide()) .addFormDataPart("user_id", userId?.toString() ?: "undefined") .addFormDataPart("can_contact", canContact.toString()) @@ -178,7 +177,7 @@ class DefaultBugReporter @Inject constructor( val requestBody = file.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull()) totalUploadedSize += requestBody.contentLength() // If we are about to upload more than the max request size, stop here - if (totalUploadedSize > ApplicationConfig.MAX_LOG_UPLOAD_SIZE) { + if (totalUploadedSize > RageshakeConfig.MAX_LOG_UPLOAD_SIZE) { Timber.e("Could not upload file ${file.name} because it would exceed the max request size") break } @@ -375,7 +374,7 @@ class DefaultBugReporter @Inject constructor( val separator = System.lineSeparator() logcatProcess.inputStream .reader() - .buffered(ApplicationConfig.MAX_LOG_UPLOAD_SIZE.toInt()) + .buffered(RageshakeConfig.MAX_LOG_UPLOAD_SIZE.toInt()) .forEachLine { line -> streamWriter.append(line) streamWriter.append(separator) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt index 5907a9a1c9..157ffb55a7 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt @@ -17,18 +17,15 @@ package io.element.android.features.rageshake.impl.reporter import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.rageshake.impl.R +import io.element.android.appconfig.RageshakeConfig import io.element.android.libraries.di.AppScope -import io.element.android.services.toolbox.api.strings.StringProvider import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import javax.inject.Inject @ContributesBinding(AppScope::class) -class DefaultBugReporterUrlProvider @Inject constructor( - private val stringProvider: StringProvider -) : BugReporterUrlProvider { +class DefaultBugReporterUrlProvider @Inject constructor() : BugReporterUrlProvider { override fun provide(): HttpUrl { - return stringProvider.getString(R.string.bug_report_url).toHttpUrl() + return RageshakeConfig.BUG_REPORT_URL.toHttpUrl() } } diff --git a/features/rageshake/impl/src/main/res/values/strings.xml b/features/rageshake/impl/src/main/res/values/strings.xml deleted file mode 100644 index dc1069570b..0000000000 --- a/features/rageshake/impl/src/main/res/values/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - https://riot.im/bugreports/submit - - element-x-android - - - diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt index a5c36df185..dae0b16bcc 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt @@ -17,15 +17,15 @@ package io.element.android.features.rageshake.impl.reporter import com.google.common.truth.Truth.assertThat -import io.element.android.services.toolbox.test.strings.FakeStringProvider +import io.element.android.appconfig.RageshakeConfig import okhttp3.HttpUrl.Companion.toHttpUrl import org.junit.Test class DefaultBugReporterUrlProviderTest { @Test fun `test DefaultBugReporterUrlProvider`() { - val sut = DefaultBugReporterUrlProvider(FakeStringProvider("https://example.org")) + val sut = DefaultBugReporterUrlProvider() val result = sut.provide() - assertThat(result).isEqualTo("https://example.org".toHttpUrl()) + assertThat(result).isEqualTo(RageshakeConfig.BUG_REPORT_URL.toHttpUrl()) } } diff --git a/libraries/dateformatter/api/src/main/AndroidManifest.xml b/libraries/dateformatter/api/src/main/AndroidManifest.xml deleted file mode 100644 index cf0e6386de..0000000000 --- a/libraries/dateformatter/api/src/main/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - diff --git a/libraries/dateformatter/impl/src/main/AndroidManifest.xml b/libraries/dateformatter/impl/src/main/AndroidManifest.xml deleted file mode 100644 index cf0e6386de..0000000000 --- a/libraries/dateformatter/impl/src/main/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - diff --git a/libraries/dateformatter/test/src/main/AndroidManifest.xml b/libraries/dateformatter/test/src/main/AndroidManifest.xml deleted file mode 100644 index cf0e6386de..0000000000 --- a/libraries/dateformatter/test/src/main/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - diff --git a/libraries/designsystem/src/main/AndroidManifest.xml b/libraries/designsystem/src/main/AndroidManifest.xml deleted file mode 100644 index 19db0c3d57..0000000000 --- a/libraries/designsystem/src/main/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/libraries/matrixui/src/main/AndroidManifest.xml b/libraries/matrixui/src/main/AndroidManifest.xml deleted file mode 100644 index 122869829c..0000000000 --- a/libraries/matrixui/src/main/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/libraries/push/impl/src/main/res/values/colors.xml b/libraries/push/impl/src/main/res/values/colors.xml index 6e04238a1a..64a9d928fb 100644 --- a/libraries/push/impl/src/main/res/values/colors.xml +++ b/libraries/push/impl/src/main/res/values/colors.xml @@ -16,7 +16,6 @@ - #368BD6 diff --git a/libraries/testtags/src/main/AndroidManifest.xml b/libraries/testtags/src/main/AndroidManifest.xml deleted file mode 100644 index ab34d2c3dd..0000000000 --- a/libraries/testtags/src/main/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/tests/uitests/build.gradle.kts b/tests/uitests/build.gradle.kts index b141b85be9..b5d0d4607e 100644 --- a/tests/uitests/build.gradle.kts +++ b/tests/uitests/build.gradle.kts @@ -67,7 +67,6 @@ dependencies { // `testOptions { unitTests.isIncludeAndroidResources = true }` in the app build.gradle.kts file // implementation(projects.app) implementation(projects.appnav) - implementation(projects.features.call) allLibrariesImpl() allServicesImpl() allFeaturesImpl(rootDir, logger) diff --git a/tools/dependencies/checkDependencies.py b/tools/dependencies/checkDependencies.py new file mode 100755 index 0000000000..cc734c9c38 --- /dev/null +++ b/tools/dependencies/checkDependencies.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +import os +import subprocess + + +def getProjectDependencies(): + print("=> Computing dependencies...") + command = subprocess.run( + ["./gradlew :app:dependencies"], + shell=True, + capture_output=True, + text=True, + ) + data = command.stdout + # Remove the trailing info like "(*)" + result = list(map(lambda x: x.split(" (")[0], data.split("\n"))) + # Filter out comment line + result = list(filter(lambda x: "--- project" in x, result)) + return result + + +def checkThatModulesExist(dependencies): + print("=> Checking that all modules exist...") + error = 0 + modules = set() + for line in dependencies: + if line: + line = line.split(" ") + for elem in line: + if ":" in elem: + modules.add(elem) + for module in modules: + path = "." + module.replace(":", "/") + "/build.gradle.kts" + if not os.path.exists(path): + error += 1 + print("Error: there is at least one dependency to '" + module + "' but the module does not exist.") + print(" Please remove occurrence(s) of 'implementation(projects" + module.replace(":", ".") + ")'.") + return error + + +def checkThatThereIsNoTestDependency(dependencies): + print("=> Checking that there are no test dependencies...") + errors = set() + currentProject = "" + for line in dependencies: + if line.startswith("+--- project "): + currentProject = line.split(" ")[2] + else: + if ":test" in currentProject: + continue + else: + subProject = line.split(" ")[-1] + if subProject.endswith(":test") or ":tests:" in subProject: + error = "Error: '" + currentProject + "' depends on the test project '" + subProject + "'\n" + error += " Please replace occurrence(s) of 'implementation(projects" + subProject.replace(":", ".") + ")'" + error += " with 'testImplementation(projects" + subProject.replace(":", ".") + ")'." + errors.add(error) + for error in errors: + print(error) + return len(errors) + + +def main(): + dependencies = getProjectDependencies() + # for dep in dependencies: + # print(dep) + errors = 0 + errors += checkThatModulesExist(dependencies) + errors += checkThatThereIsNoTestDependency(dependencies) + print() + if (errors == 0): + print("All checks passed successfully.") + elif (errors == 1): + print("Please fix the error above.") + else: + print("Please fix the " + str(errors) + " errors above.") + exit(errors) + + +if __name__ == "__main__": + main()