Compare commits

..

12 Commits

Author SHA1 Message Date
Ricki Hirner
a30dc63714 Update AndroidManifest to set install location to internal only (again) 2026-02-06 14:36:19 +01:00
Ricki Hirner
93431ee6d5 Remove build.gradle.kts and update settings.gradle.kts
- Remove `build.gradle.kts` file
- Update `settings.gradle.kts` to use `gradlePluginPortal` for plugins
- Set `repositoriesMode` to `PREFER_SETTINGS`
- Add comments for clarity in `settings.gradle.kts`
2026-02-06 14:30:28 +01:00
Ricki Hirner
31c4eb6380 Update package references and permissions
- Update references from `com.davx5.ose.App` to `at.bitfire.davdroid.CoreApp`
- Enable archive naming in `app-ose/build.gradle.kts`
- Remove redundant permission declarations in `core/src/main/AndroidManifest.xml`
2026-02-06 14:14:23 +01:00
Ricki Hirner
6b6c340844 Refactor App Initialization
- Introduce `CoreApp` for common initialization logic
- Refactor `App` to extend `CoreApp` and handle WorkManager configuration
2026-02-06 14:04:11 +01:00
Ricki Hirner
e1a58ef576 Don't run app-ose tests yet because there are none 2026-02-06 11:18:49 +01:00
Ricki Hirner
ec00cc6a3f Move WorkManagerInitializer from core to app-ose AndroidManifest 2026-02-06 11:11:48 +01:00
Ricki Hirner
2ce9b83356 Fix core instrumentation tests 2026-02-06 10:57:01 +01:00
Ricki Hirner
47b4ecd705 Add OpenID AppAuth library 2026-02-05 20:06:37 +01:00
Ricki Hirner
bced7e5ee5 Update test configuration
- Add managed device for testing
- Set device to "Pixel 3" with API level 34
- Use AOSP system image source
2026-02-05 20:00:17 +01:00
Ricki Hirner
360c2249cf Update test workflows 2026-02-05 19:55:42 +01:00
Ricki Hirner
53791871c6 Split core and app-ose 2026-02-05 18:16:35 +01:00
Ricki Hirner
9bddf4e8d4 Rename "app" subproject to core 2026-02-05 17:30:18 +01:00
527 changed files with 380 additions and 138 deletions

View File

@@ -49,10 +49,10 @@ jobs:
# Cache configurations for the other jobs (including assemble for CodeQL)
- name: Populate configuration cache
run: |
./gradlew --dry-run app:assembleDebug
./gradlew --dry-run app:lintOseDebug
./gradlew --dry-run app:testOseDebugUnitTest
./gradlew --dry-run app:virtualOseDebugAndroidTest
./gradlew --dry-run core:assembleDebug app:assembleDebug
./gradlew --dry-run core:lintDebug app:lintOseDebug
./gradlew --dry-run core:testDebugUnitTest
./gradlew --dry-run core:virtualDebugAndroidTest
unit_tests:
needs: compile
@@ -76,10 +76,10 @@ jobs:
key: android-${{ hashFiles('app/build.gradle.kts') }}
- name: Lint checks
run: ./gradlew app:lintOseDebug
run: ./gradlew core:lintDebug app:lintOseDebug
- name: Unit tests
run: ./gradlew app:testOseDebugUnitTest
run: ./gradlew core:testDebugUnitTest
instrumented_tests:
needs: compile
@@ -126,7 +126,7 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Instrumented tests
run: ./gradlew app:virtualOseDebugAndroidTest
run: ./gradlew core:virtualDebugAndroidTest
- name: Cache AVD
uses: actions/cache/save@v5

126
app-ose/build.gradle.kts Normal file
View File

@@ -0,0 +1,126 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
}
android {
compileSdk = 36
defaultConfig {
minSdk = 24 // Android 7.0
targetSdk = 36 // Android 16
applicationId = "at.bitfire.davdroid"
versionCode = 405090005
versionName = "4.5.9"
base.archivesName = "davx5-$versionCode-$versionName"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
buildFeatures {
compose = true
}
// Java namespace for our classes (not to be confused with Android package ID)
namespace = "com.davx5.ose"
flavorDimensions += "distribution"
productFlavors {
create("ose") {
dimension = "distribution"
versionNameSuffix = "-ose"
}
}
androidResources {
generateLocaleConfig = true
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules-release.pro")
isShrinkResources = true
signingConfig = signingConfigs.findByName("bitfire")
}
}
signingConfigs {
create("bitfire") {
storeFile = file(System.getenv("ANDROID_KEYSTORE") ?: "/dev/null")
storePassword = System.getenv("ANDROID_KEYSTORE_PASSWORD")
keyAlias = System.getenv("ANDROID_KEY_ALIAS")
keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
}
}
@Suppress("UnstableApiUsage")
testOptions {
managedDevices {
localDevices {
create("virtual") {
device = "Pixel 3"
// TBD: API level 35 and higher causes network tests to fail sometimes, see https://github.com/bitfireAT/davx5-ose/issues/1525
// Suspected reason: https://developer.android.com/about/versions/15/behavior-changes-all#background-network-access
apiLevel = 34
systemImageSource = "aosp-atd"
}
}
}
}
}
dependencies {
// include core subproject (manages its own dependencies itself, however from same version catalog)
implementation(project(":core"))
// Kotlin / Android
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines)
coreLibraryDesugaring(libs.android.desugaring)
// Hilt
implementation(libs.hilt.android.base)
ksp(libs.androidx.hilt.compiler)
ksp(libs.hilt.android.compiler)
// support libs
implementation(libs.androidx.core)
implementation(libs.androidx.hilt.work)
implementation(libs.androidx.lifecycle.viewmodel.base)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.work.base)
// Jetpack Compose
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material3)
debugImplementation(libs.androidx.compose.ui.tooling)
implementation(libs.androidx.compose.ui.toolingPreview)
// own libraries
implementation(libs.bitfire.cert4android)
// third-party libs
implementation(libs.guava)
implementation(libs.okhttp.base)
implementation(libs.openid.appauth)
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="internalOnly">
<application android:name=".App">
<!-- Required for Hilt/WorkManager integration. See
- https://developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration#remove-default
- https://developer.android.com/training/dependency-injection/hilt-jetpack#workmanager
However, we must not disable AndroidX startup completely, as it's needed by other libraries like okhttp. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
</application>
</manifest>

View File

@@ -0,0 +1,30 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package com.davx5.ose
import androidx.work.Configuration
import at.bitfire.davdroid.CoreApp
import dagger.hilt.android.HiltAndroidApp
/**
* Actual implementation of Application, used for Hilt. Delegates to [CoreApp].
*/
@HiltAndroidApp
class App: CoreApp(), Configuration.Provider {
/**
* Required for Hilt/WorkManager integration, see:
* https://developer.android.com/training/dependency-injection/hilt-jetpack#workmanager
*
* This requires to remove the androidx.work.WorkManagerInitializer from App Startup
* in the AndroidManifest, see:
* https://developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration#remove-default
*/
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}

View File

@@ -7,7 +7,7 @@ package com.davx5.ose.di
import androidx.compose.material3.ColorScheme
import at.bitfire.davdroid.di.scope.DarkColorScheme
import at.bitfire.davdroid.di.scope.LightColorScheme
import com.davx5.ose.ui.OseTheme
import at.bitfire.davdroid.ui.OseTheme
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -15,7 +15,7 @@ import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
class OseColorSchemesModule {
class ColorSchemesModule {
@Provides
@LightColorScheme

View File

@@ -11,7 +11,7 @@ import at.bitfire.davdroid.ui.intro.IntroPageFactory
import at.bitfire.davdroid.ui.setup.LoginTypesProvider
import com.davx5.ose.ui.about.OpenSourceLicenseInfoProvider
import com.davx5.ose.ui.intro.OseIntroPageFactory
import com.davx5.ose.ui.setup.StandardLoginTypesProvider
import at.bitfire.davdroid.ui.setup.StandardLoginTypesProvider
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn

View File

@@ -4,7 +4,7 @@
package com.davx5.ose.ui.about
import android.app.Application
import android.content.Context
import android.text.Spanned
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -14,14 +14,16 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.text.HtmlCompat
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import at.bitfire.davdroid.di.scope.IoDispatcher
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
import at.bitfire.davdroid.ui.about.AboutActivity
import com.google.common.io.CharStreams
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -41,13 +43,16 @@ class OpenSourceLicenseInfoProvider @Inject constructor(): AboutActivity.AppLice
@HiltViewModel
class Model @Inject constructor(app: Application): AndroidViewModel(app) {
class Model @Inject constructor(
@ApplicationContext private val context: Context,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
): ViewModel() {
var gpl by mutableStateOf<Spanned?>(null)
init {
viewModelScope.launch(Dispatchers.IO) {
app.resources.assets.open("gplv3.html").use { inputStream ->
viewModelScope.launch(ioDispatcher) {
context.resources.assets.open("gplv3.html").use { inputStream ->
val raw = CharStreams.toString(inputStream.bufferedReader())
gpl = HtmlCompat.fromHtml(raw, HtmlCompat.FROM_HTML_MODE_LEGACY)
}

View File

@@ -1,12 +0,0 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.hilt) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.mikepenz.aboutLibraries.android) apply false
}

View File

View File

@@ -3,7 +3,7 @@
*/
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.android.library)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.serialization)
@@ -16,15 +16,7 @@ android {
compileSdk = 36
defaultConfig {
applicationId = "at.bitfire.davdroid"
versionCode = 405090005
versionName = "4.5.9"
base.archivesName = "davx5-$versionCode-$versionName"
minSdk = 24 // Android 7.0
targetSdk = 36 // Android 16
testInstrumentationRunner = "at.bitfire.davdroid.HiltTestRunner"
}
@@ -50,37 +42,9 @@ android {
// Java namespace for our classes (not to be confused with Android package ID)
namespace = "at.bitfire.davdroid"
flavorDimensions += "distribution"
productFlavors {
create("ose") {
dimension = "distribution"
versionNameSuffix = "-ose"
}
}
sourceSets {
getByName("androidTest") {
assets.srcDir("$projectDir/schemas")
}
}
signingConfigs {
create("bitfire") {
storeFile = file(System.getenv("ANDROID_KEYSTORE") ?: "/dev/null")
storePassword = System.getenv("ANDROID_KEYSTORE_PASSWORD")
keyAlias = System.getenv("ANDROID_KEY_ALIAS")
keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules-release.pro")
isShrinkResources = true
signingConfig = signingConfigs.findByName("bitfire")
isMinifyEnabled = false
}
}
@@ -88,10 +52,6 @@ android {
disable += arrayOf("GoogleAppIndexingWarning", "ImpliedQuantity", "MissingQuantity", "MissingTranslation", "ExtraTranslation", "RtlEnabled", "RtlHardcoded", "Typos")
}
androidResources {
generateLocaleConfig = true
}
packaging {
resources {
// multiple (test) dependencies have LICENSE files at same location
@@ -99,6 +59,12 @@ android {
}
}
sourceSets {
getByName("androidTest") {
assets.srcDir("$projectDir/schemas")
}
}
@Suppress("UnstableApiUsage")
testOptions {
managedDevices {
@@ -127,7 +93,7 @@ aboutLibraries {
}
dependencies {
// core
// Kotlin / Android
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines)
coreLibraryDesugaring(libs.android.desugaring)

View File

View File

@@ -0,0 +1,28 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid.di
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.cert4android.CustomCertStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import java.util.Optional
@Module
@InstallIn(SingletonComponent::class)
class Cert4AndroidModule {
@Provides
fun customCertManager(): Optional<CustomCertManager> = Optional.empty()
@Provides
fun customHostnameVerifier(): Optional<CustomCertManager.HostnameVerifier> = Optional.empty()
@Provides
fun customCertStore(): Optional<CustomCertStore> = Optional.empty()
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid.di
import androidx.compose.material3.ColorScheme
import at.bitfire.davdroid.di.scope.DarkColorScheme
import at.bitfire.davdroid.di.scope.LightColorScheme
import at.bitfire.davdroid.ui.OseTheme
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
class ColorSchemesModule {
@Provides
@LightColorScheme
fun lightColorScheme(): ColorScheme = OseTheme.lightScheme
@Provides
@DarkColorScheme
fun darkColorScheme(): ColorScheme = OseTheme.darkScheme
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid.di
import at.bitfire.davdroid.ui.AccountsDrawerHandler
import at.bitfire.davdroid.ui.OseAccountsDrawerHandler
import at.bitfire.davdroid.ui.about.AboutActivity
import at.bitfire.davdroid.ui.about.FakeAppLicenseInfoProvider
import at.bitfire.davdroid.ui.intro.FakeIntroPageFactory
import at.bitfire.davdroid.ui.intro.IntroPageFactory
import at.bitfire.davdroid.ui.setup.LoginTypesProvider
import at.bitfire.davdroid.ui.setup.StandardLoginTypesProvider
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.components.SingletonComponent
interface OseModules {
@Module
@InstallIn(ActivityComponent::class)
interface ForActivities {
@Binds
fun accountsDrawerHandler(impl: OseAccountsDrawerHandler): AccountsDrawerHandler
@Binds
fun loginTypesProvider(impl: StandardLoginTypesProvider): LoginTypesProvider
}
@Module
@InstallIn(ViewModelComponent::class)
interface ForViewModels {
@Binds
fun appLicenseInfoProvider(impl: FakeAppLicenseInfoProvider): AboutActivity.AppLicenseInfoProvider
@Binds
fun loginTypesProvider(impl: StandardLoginTypesProvider): LoginTypesProvider
}
@Module
@InstallIn(SingletonComponent::class)
interface Global {
@Binds
fun introPageFactory(impl: FakeIntroPageFactory): IntroPageFactory
}
}

Some files were not shown because too many files have changed in this diff Show More