diff --git a/.gitignore b/.gitignore index 4f42dd7f6..5a01d97a5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,22 +3,10 @@ Thumbs.db # IDEs and editors +.gradle .idea/ -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# Visual Studio Code -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# Node -node_modules -npm-debug.log +*.iml +/build +/captures +/local.properties +local.properties diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 000000000..e994582f1 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,129 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.detekt) + alias(libs.plugins.hilt) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.ksp) + kotlin("kapt") +} + +android { + namespace = "com.x8bit.bitwarden" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + applicationId = "com.x8bit.bitwarden" + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility(libs.versions.jvmTarget.get()) + targetCompatibility(libs.versions.jvmTarget.get()) + } + kotlinOptions { + jvmTarget = libs.versions.jvmTarget.get() + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExtensionVersion.get() + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + @Suppress("UnstableApiUsage") + testOptions { + // Required for Robolectric + unitTests.isIncludeAndroidResources = true + unitTests.isReturnDefaultValues = true + } +} + +dependencies { + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.compose.animation) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.runtime) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.hilt.navigation.compose) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.navigation.compose) + ksp(libs.androidx.room.compiler) + implementation(libs.androidx.room.ktx) + implementation(libs.androidx.room.runtime) + implementation(platform(libs.google.firebase.bom)) + implementation(libs.bumptech.glide) + implementation(libs.google.firebase.cloud.messaging) + implementation(libs.google.firebase.crashlytics) + implementation(libs.google.hilt.android) + kapt(libs.google.hilt.compiler) + implementation(libs.jakewharton.retrofit.kotlinx.serialization) + implementation(libs.kotlinx.coroutines.android) + implementation(libs.kotlinx.serialization) + implementation(libs.nulab.zxcvbn4j) + implementation(libs.square.okhttp) + implementation(libs.square.okhttp.logging) + implementation(libs.square.retrofit) + implementation(libs.zxing.zxing.core) + + // For now we are restricted to running Compose tests for debug builds only + debugImplementation(libs.androidx.compose.ui.test.manifest) + debugImplementation(libs.androidx.compose.ui.tooling) + + testImplementation(libs.androidx.compose.ui.test) + testImplementation(libs.google.hilt.android.testing) + testImplementation(libs.junit.junit5) + testImplementation(libs.junit.vintage) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.mockk.mockk) + testImplementation(libs.robolectric.robolectric) + testImplementation(libs.square.turbine) + + detektPlugins(libs.detekt.detekt.formatting) + detektPlugins(libs.detekt.detekt.rules) +} + +detekt { + autoCorrect = true + config.from(files("$rootDir/detekt-config.yml")) +} + +tasks { + getByName("check") { + // Add detekt with type resolution to check + dependsOn("detektMain") + } + + withType().configureEach { + jvmTarget = libs.versions.jvmTarget.get() + } + withType().configureEach { + jvmTarget = libs.versions.jvmTarget.get() + } + + withType { + useJUnitPlatform() + } +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 000000000..90d74aaa7 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,64 @@ +################################################################################ +# Firebase Crashlytics +################################################################################ + +# Keep file names and line numbers. +-keepattributes SourceFile,LineNumberTable + +# Keep custom exceptions. +-keep public class * extends java.lang.Exception + +################################################################################ +# kotlinx.serialization +################################################################################ + +-keepattributes *Annotation*, InnerClasses + +# JsonElement serializers +-keep,includedescriptorclasses class kotlinx.serialization.json.**$$serializer { *; } +-keep,includedescriptorclasses class com.x8bit.bitwarden.**$$serializer { *; } +-keepclassmembers class com.x8bit.bitwarden.** { + *** Companion; +} +-keepclasseswithmembers class om.x8bit.bitwarden.** { + kotlinx.serialization.KSerializer serializer(...); +} + +# kotlinx-serialization-json specific. +-keepclassmembers class kotlinx.serialization.json.** { + *** Companion; +} +-keepclasseswithmembers class kotlinx.serialization.json.** { + kotlinx.serialization.KSerializer serializer(...); +} + +################################################################################ +# Glide +################################################################################ + +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule + +################################################################################ +# Okhttp/Retrofit https://square.github.io/okhttp/ & https://square.github.io/retrofit/ +################################################################################ + +# https://github.com/square/okhttp/blob/339732e3a1b78be5d792860109047f68a011b5eb/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro#L11-L14 +-dontwarn okhttp3.internal.platform.** +-dontwarn org.bouncycastle.** +# Related to this issue on https://github.com/square/retrofit/issues/3880 +# Check https://github.com/square/retrofit/tags for new versions +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response +# This solves this issue https://github.com/square/retrofit/issues/3880 +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +################################################################################ +# ZXing +################################################################################ + +# Suppress zxing missing class error due to circular references +-dontwarn com.google.zxing.BarcodeFormat +-dontwarn com.google.zxing.EncodeHintType +-dontwarn com.google.zxing.MultiFormatWriter +-dontwarn com.google.zxing.common.BitMatrix diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3c02b7e9b --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/x8bit/bitwarden/BitwardenApplication.kt b/app/src/main/java/com/x8bit/bitwarden/BitwardenApplication.kt new file mode 100644 index 000000000..4dc93bd8d --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/BitwardenApplication.kt @@ -0,0 +1,10 @@ +package com.x8bit.bitwarden + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +/** + * Custom application class. + */ +@HiltAndroidApp +class BitwardenApplication : Application() diff --git a/app/src/main/java/com/x8bit/bitwarden/MainActivity.kt b/app/src/main/java/com/x8bit/bitwarden/MainActivity.kt new file mode 100644 index 000000000..93fdfff88 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/MainActivity.kt @@ -0,0 +1,35 @@ +package com.x8bit.bitwarden + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import com.x8bit.bitwarden.ui.theme.BitwardenTheme +import dagger.hilt.android.AndroidEntryPoint + +/** + * Primary entry point for the application. + */ +@AndroidEntryPoint +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + BitwardenTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Text( + text = stringResource(id = R.string.app_name), + ) + } + } + } + } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/theme/Theme.kt b/app/src/main/java/com/x8bit/bitwarden/ui/theme/Theme.kt new file mode 100644 index 000000000..2956c2fc3 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/theme/Theme.kt @@ -0,0 +1,74 @@ +package com.x8bit.bitwarden.ui.theme + +import android.app.Activity +import android.content.Context +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat +import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.R.color + +/** + * The overall application theme. This can be configured to support a [darkTheme] and + * [dynamicColor]. + */ +@Composable +fun BitwardenTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = false, + content: @Composable () -> Unit, +) { + // Get the current scheme + val context = LocalContext.current + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> darkColorScheme(context) + else -> lightColorScheme(context) + } + + // Update status bar according to scheme + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + + // Set overall theme based on color scheme and typography settings + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content, + ) +} + +private fun darkColorScheme(context: Context): ColorScheme = + darkColorScheme( + primary = Color(context.getColor(color.dark_primary)), + secondary = Color(context.getColor(R.color.dark_primary)), + tertiary = Color(context.getColor(R.color.dark_primary)), + ) + +private fun lightColorScheme(context: Context): ColorScheme = + lightColorScheme( + primary = Color(context.getColor(color.primary)), + secondary = Color(context.getColor(R.color.primary)), + tertiary = Color(context.getColor(R.color.primary)), + ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/theme/Type.kt b/app/src/main/java/com/x8bit/bitwarden/ui/theme/Type.kt new file mode 100644 index 000000000..fb0c365f5 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/theme/Type.kt @@ -0,0 +1,17 @@ +package com.x8bit.bitwarden.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +val Typography: Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp, + ), +) diff --git a/app/src/main/res/drawable-hdpi/logo_legacy.png b/app/src/main/res/drawable-hdpi/logo_legacy.png new file mode 100644 index 000000000..c1f80693e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/logo_legacy.png differ diff --git a/app/src/main/res/drawable-hdpi/logo_white_legacy.png b/app/src/main/res/drawable-hdpi/logo_white_legacy.png new file mode 100644 index 000000000..d476025e8 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/logo_white_legacy.png differ diff --git a/app/src/main/res/drawable-night-v26/splash_screen_round.xml b/app/src/main/res/drawable-night-v26/splash_screen_round.xml new file mode 100644 index 000000000..7737f6476 --- /dev/null +++ b/app/src/main/res/drawable-night-v26/splash_screen_round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable-v26/splash_screen_round.xml b/app/src/main/res/drawable-v26/splash_screen_round.xml new file mode 100644 index 000000000..602f055dd --- /dev/null +++ b/app/src/main/res/drawable-v26/splash_screen_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/logo_legacy.png b/app/src/main/res/drawable-xhdpi/logo_legacy.png new file mode 100644 index 000000000..b8d376492 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/logo_legacy.png differ diff --git a/app/src/main/res/drawable-xhdpi/logo_white_legacy.png b/app/src/main/res/drawable-xhdpi/logo_white_legacy.png new file mode 100644 index 000000000..d3d9f5805 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/logo_white_legacy.png differ diff --git a/app/src/main/res/drawable-xxhdpi/logo_legacy.png b/app/src/main/res/drawable-xxhdpi/logo_legacy.png new file mode 100644 index 000000000..904731676 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/logo_legacy.png differ diff --git a/app/src/main/res/drawable-xxhdpi/logo_white_legacy.png b/app/src/main/res/drawable-xxhdpi/logo_white_legacy.png new file mode 100644 index 000000000..7d76d07d2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/logo_white_legacy.png differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..2339c04bd --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..e4d87cace --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_monochrome.xml b/app/src/main/res/drawable/ic_launcher_monochrome.xml new file mode 100644 index 000000000..5a54380d9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_monochrome.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/logo_rounded.xml b/app/src/main/res/drawable/logo_rounded.xml new file mode 100644 index 000000000..860d4c963 --- /dev/null +++ b/app/src/main/res/drawable/logo_rounded.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/drawable/splash_screen.xml b/app/src/main/res/drawable/splash_screen.xml new file mode 100644 index 000000000..56530dc15 --- /dev/null +++ b/app/src/main/res/drawable/splash_screen.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/splash_screen_dark.xml b/app/src/main/res/drawable/splash_screen_dark.xml new file mode 100644 index 000000000..6e5563e7d --- /dev/null +++ b/app/src/main/res/drawable/splash_screen_dark.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 000000000..c78bee3b5 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 000000000..1084c2408 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..2648f2cdd Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..41064311e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..408bdf117 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..5f02b375f Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..7973834d3 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..e9af08b5d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..47aff4db5 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..807c39195 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..87a12fc0e Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..0bd89b726 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..53fa6f596 --- /dev/null +++ b/app/src/main/res/values-night/styles.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..b8d0b3952 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ + + + + #dddddd + #1452BC + #175DDC + + + #666666 + #191919 + #191919 + #52bdfb + + + #000000 + #333333 + #738182 + #efeff4 + #FFFFFF + diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..a8a787f8f --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #175DDC + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..193af8b68 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Bitwarden + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..87e723c8f --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 000000000..a608293f2 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 000000000..288f6904b --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/test/java/com/x8bit/bitwarden/example/ExampleComposeTest.kt b/app/src/test/java/com/x8bit/bitwarden/example/ExampleComposeTest.kt new file mode 100644 index 000000000..524d117f6 --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/example/ExampleComposeTest.kt @@ -0,0 +1,45 @@ +package com.x8bit.bitwarden.example + +import androidx.compose.material3.Button +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performClick +import dagger.hilt.android.testing.HiltTestApplication +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Example showing that Compose tests using "junit" imports and Roboelectric work. + */ +@Config( + application = HiltTestApplication::class, + sdk = [Config.NEWEST_SDK], +) +@RunWith(RobolectricTestRunner::class) +class ExampleComposeTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun `the onClick callback should be correctly triggered when performing a click`() { + var isClicked = false + composeTestRule.setContent { + Button( + onClick = { isClicked = true }, + ) { + // Empty + } + } + + assertFalse(isClicked) + + composeTestRule.onRoot().performClick() + + assertTrue(isClicked) + } +} diff --git a/app/src/test/java/com/x8bit/bitwarden/example/ExampleJUnit5Test.kt b/app/src/test/java/com/x8bit/bitwarden/example/ExampleJUnit5Test.kt new file mode 100644 index 000000000..d6e86d59a --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/example/ExampleJUnit5Test.kt @@ -0,0 +1,18 @@ +package com.x8bit.bitwarden.example + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +/** + * Example showing that JUnit5 tests using "jupiter" imports work. + */ +class ExampleJUnit5Test { + @Nested + inner class NestedSample { + @Test + fun `an empty listOf should be the same as emptyList`() { + assertEquals(listOf(), emptyList()) + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..3f2fd1c38 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.hilt) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.ksp) apply false +} diff --git a/detekt-config.yml b/detekt-config.yml new file mode 100644 index 000000000..838787ea8 --- /dev/null +++ b/detekt-config.yml @@ -0,0 +1,668 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + +config: + validation: true + warningsAsErrors: false + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FileBasedFindingsReport' + +output-reports: + active: true + exclude: + +comments: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + UndocumentedPublicClass: + active: true + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + active: true + ignoreAnnotated: [ 'Module' ] + UndocumentedPublicProperty: + active: false + +complexity: + active: true + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: true + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: [ run, let, apply, with, also, use, forEach, isNotNull, ifNull ] + LabeledExpression: + active: false + ignoredLabels: [ ] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotated: [ 'Composable' ] + MethodOverloading: + active: false + threshold: 6 + NestedBlockDepth: + active: true + threshold: 4 + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + ignoreAnnotated: [ 'Module' ] + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + RedundantSuspendModifier: + active: false + SuspendFunWithFlowReturnType: + active: false + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: false + methodNames: [ toString, hashCode, equals, finalize ] + InstanceOfCheckForException: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + NotImplementedDeclaration: + active: false + PrintStackTrace: + active: false + RethrowCaughtException: + active: false + ReturnFromFinally: + active: false + ignoreLabeled: false + SwallowedException: + active: false + ignoredExceptionTypes: + - InterruptedException + - NumberFormatException + - ParseException + - MalformedURLException + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: false + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + exceptions: + - IllegalArgumentException + - IllegalStateException + - IOException + ThrowingNewInstanceOfSameException: + active: false + TooGenericExceptionCaught: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + exceptionNames: + - ArrayIndexOutOfBoundsException + - Error + - Exception + - IllegalMonitorStateException + - NullPointerException + - IndexOutOfBoundsException + - RuntimeException + - Throwable + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - Error + - Exception + - Throwable + - RuntimeException + +formatting: + active: true + android: false + autoCorrect: true + AnnotationOnSeparateLine: + active: false + autoCorrect: true + AnnotationSpacing: + active: false + autoCorrect: true + ArgumentListWrapping: + active: false + autoCorrect: true + ChainWrapping: + active: true + autoCorrect: true + CommentSpacing: + active: true + autoCorrect: true + EnumEntryNameCase: + active: false + autoCorrect: true + Filename: + active: false + FinalNewline: + active: true + autoCorrect: true + insertFinalNewLine: true + ImportOrdering: + active: false + autoCorrect: true + layout: 'idea' + Indentation: + active: false + autoCorrect: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ModifierOrdering: + active: true + autoCorrect: true + MultiLineIfElse: + active: true + autoCorrect: true + NoBlankLineBeforeRbrace: + active: true + autoCorrect: true + NoConsecutiveBlankLines: + active: true + autoCorrect: true + NoEmptyClassBody: + active: true + autoCorrect: true + NoEmptyFirstLineInMethodBlock: + active: false + autoCorrect: true + NoLineBreakAfterElse: + active: true + autoCorrect: true + NoLineBreakBeforeAssignment: + active: true + autoCorrect: true + NoMultipleSpaces: + active: true + autoCorrect: true + NoSemicolons: + active: true + autoCorrect: true + NoTrailingSpaces: + active: true + autoCorrect: true + NoUnitReturn: + active: true + autoCorrect: true + NoUnusedImports: + active: true + autoCorrect: true + NoWildcardImports: + active: true + PackageName: + active: true + autoCorrect: true + ParameterListWrapping: + active: true + autoCorrect: true + SpacingAroundColon: + active: true + autoCorrect: true + SpacingAroundComma: + active: true + autoCorrect: true + SpacingAroundCurly: + active: true + autoCorrect: true + SpacingAroundDot: + active: true + autoCorrect: true + SpacingAroundDoubleColon: + active: false + autoCorrect: true + SpacingAroundKeyword: + active: true + autoCorrect: true + SpacingAroundOperators: + active: true + autoCorrect: true + SpacingAroundParens: + active: true + autoCorrect: true + SpacingAroundRangeOperator: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: false + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: false + autoCorrect: true + StringTemplate: + active: true + autoCorrect: true + TrailingCommaOnCallSite: + active: true + autoCorrect: true + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: true + autoCorrect: true + useTrailingCommaOnDeclarationSite: true + +libraries: + active: true + ForbiddenPublicDataClass: + active: false + LibraryCodeMustSpecifyReturnType: + active: true + LibraryEntitiesShouldNotBePublic: + active: false + +naming: + active: true + ClassNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + forbiddenName: [ ] + FunctionMaxLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' + excludeClassPattern: '$^' + ignoreAnnotated: [ 'Composable' ] + FunctionParameterNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + InvalidPackageDeclaration: + active: false + rootPackage: '' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ObjectPropertyNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/compose/**'] + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + maximumVariableNameLength: 64 + VariableMinLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + minimumVariableNameLength: 1 + VariableNaming: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: true + ArrayPrimitive: + active: true + ForEachOnRange: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + SpreadOperator: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + Deprecation: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: false + IgnoredReturnValue: + active: false + restrictToConfig: true + returnValueAnnotations: [ '*.CheckReturnValue', '*.CheckResult' ] + ImplicitDefaultLocale: + active: false + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ignoreAnnotated: [ ] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: false + NullableToStringCall: + active: false + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: false + UnnecessarySafeCall: + active: false + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + UnsafeCast: + active: false + UselessPostfixExpression: + active: false + WrongEqualsTypeParameter: + active: true + +style: + active: true + BracesOnIfStatements: + active: false + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: [ 'to' ] + DataClassShouldBeImmutable: + active: false + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: false + ExpressionBodySyntax: + active: false + includeLineWrapping: false + ForbiddenComment: + active: true + comments: [ 'FIXME:', 'STOPSHIP:' ] + allowedPatterns: '' + ForbiddenImport: + active: false + imports: [ ] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: [ 'kotlin.io.println', 'kotlin.io.print' ] + ForbiddenVoid: + active: false + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + excludedFunctions: [ 'describeContents' ] + ignoreAnnotated: [ 'dagger.Provides' ] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ignoreNumbers: [ '-1', '0', '0.5', '1', '2' ] + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + MandatoryBracesLoops: + active: false + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + NestedClassesVisibility: + active: false + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + OptionalWhenBraces: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: true + max: 2 + excludedFunctions: [ 'equals' ] + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: false + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: true + max: 2 + TrailingWhitespace: + active: false + UnderscoresInNumericLiterals: + active: false + acceptableLength: 5 + UnnecessaryAbstractClass: + active: true + ignoreAnnotated: [ 'Module' ] + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: false + UnnecessaryInheritance: + active: true + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: false + allowedNames: '(_|ignored|expected|serialVersionUID)' + UseArrayLiteralsInAnnotations: + active: false + UseCheckNotNull: + active: false + UseCheckOrError: + active: false + UseDataClass: + active: false + ignoreAnnotated: [ ] + allowVars: false + UseEmptyCounterpart: + active: false + UseIfInsteadOfWhen: + active: false + UseRequire: + active: false + UseRequireNotNull: + active: false + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + ignoreAnnotated: [ 'Module' ] + VarCouldBeVal: + active: false + WildcardImport: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludeImports: [ 'java.util.*', 'kotlinx.android.synthetic.*' ] diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..d8d780060 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,8 @@ +android.nonTransitiveRClass=true +# Suppresses warning until build tools that are compatible with SDK 34 +android.suppressUnsupportedCompileSdk=34 +android.useAndroidX=true + +kotlin.code.style=official + +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..3edb81270 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,89 @@ +# Please keep each version / library / plugin alphabetized within its own section and subsubsection. +[versions] + +# SDK Versions +compileSdk = "34" +targetSdk = "34" +minSdk = "28" + +# Dependency Versions +accompanist = "0.30.1" +androidGradlePlugin = "8.1.0" +androidxActivity = "1.7.2" +androidxComposeBom = "2023.06.01" +androidxCore = "1.10.1" +androidxHiltNavigationCompose = "1.0.0" +androidxLifecycle = "2.6.1" +androidxNavigation = "2.6.0" +androidxRoom = "2.5.2" +detekt = "1.23.1" +firebaseBom = "32.2.2" +glide = "4.15.1" +hilt = "2.47" +junit5 = "5.8.2" +jvmTarget = "1.8" +kotlin = "1.9.0" +kotlinCompilerExtensionVersion = "1.5.1" +kotlinxCoroutines = "1.7.3" +kotlinxSerialization = "1.5.1" +ksp = "1.9.0-1.0.11" +mockk = "1.13.5" +okhttp = "4.11.0" +retrofit = "2.9.0" +retrofitKotlinxSerialization = "1.0.0" +roboelectric = "4.10.3" +turbine = "1.0.0" +zxcvbn4j = "1.8.0" +zxing = "3.5.2" + +[libraries] +# Format: - +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" } +androidx-compose-animation = { module = "androidx.compose.animation:animation" } +androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidxComposeBom" } +androidx-compose-material3 = { module = "androidx.compose.material3:material3" } +androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" } +androidx-compose-ui = { module = "androidx.compose.ui:ui" } +androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } +androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4" } +androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } +androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } +androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } +androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidxRoom" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidxRoom" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidxRoom" } +bumptech-glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } +detekt-detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detekt-detekt-rules = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" } +google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } +google-firebase-cloud-messaging = { module = "com.google.firebase:firebase-messaging-ktx" } +google-firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" } +google-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } +google-hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } +google-hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } +jakewharton-retrofit-kotlinx-serialization = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerialization" } +junit-junit5 = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit5" } +junit-vintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } +kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } +mockk-mockk = { module = "io.mockk:mockk", version.ref = "mockk" } +nulab-zxcvbn4j = { module = "com.nulab-inc:zxcvbn", version.ref = "zxcvbn4j" } +robolectric-robolectric = { module = "org.robolectric:robolectric", version.ref = "roboelectric" } +square-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +square-okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" } +square-retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } +square-turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } +zxing-zxing-core = { module = "com.google.zxing:core", version.ref = "zxing" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..e708b1c02 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..22f100f75 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Aug 09 17:13:50 CDT 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..4f906e0c8 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# 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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..b79d916cf --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "Bitwarden" +include(":app")