diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 6912606eb0..b0656fb0aa 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { compileOnly(libs.firebase.crashlytics.gradlePlugin) compileOnly(libs.firebase.performance.gradlePlugin) compileOnly(libs.kotlin.gradlePlugin) -// compileOnly(libs.compose.gradlePlugin) + compileOnly(libs.compose.gradlePlugin) compileOnly(libs.ksp.gradlePlugin) compileOnly(libs.room.gradlePlugin) compileOnly(libs.detekt.gradlePlugin) @@ -118,5 +118,19 @@ gradlePlugin { implementationClass = "MifosGitHooksConventionPlugin" description = "Installs git hooks for the project" } + + // KMP & CMP Plugins + register("cmpFeature") { + id = "mifos.cmp.feature" + implementationClass = "CMPFeatureConventionPlugin" + } + register("kmpKoin") { + id = "mifos.kmp.koin" + implementationClass = "KMPKoinConventionPlugin" + } + register("kmpLibrary") { + id = "mifos.kmp.library" + implementationClass = "KMPLibraryConventionPlugin" + } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt index 1166a9b977..2fdf12b9db 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt @@ -9,7 +9,7 @@ class AndroidApplicationComposeConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { apply(plugin = "com.android.application") -// apply(plugin = "org.jetbrains.kotlin.plugin.compose") + apply(plugin = "org.jetbrains.kotlin.plugin.compose") val extension = extensions.getByType() configureAndroidCompose(extension) diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt index 24cf93d94b..ff41985a31 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt @@ -9,7 +9,7 @@ class AndroidLibraryComposeConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { apply(plugin = "com.android.library") -// apply(plugin = "org.jetbrains.kotlin.plugin.compose") + apply(plugin = "org.jetbrains.kotlin.plugin.compose") val extension = extensions.getByType() configureAndroidCompose(extension) diff --git a/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt new file mode 100644 index 0000000000..ca45ce1b95 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt @@ -0,0 +1,56 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies +import org.mifos.libs + +class CMPFeatureConventionPlugin : Plugin { + + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("mifos.kmp.libray") + apply("mifos.kmp.koin") + apply("org.jetbrains.kotlin.plugin.compose") + apply("org.jetbrains.compose") + } + + dependencies { + add("commonMainImplementation", project(":core:ui")) + add("commonMainImplementation", project(":core:designsystem")) + add("commonMainImplementation", project(":core:testing")) + add("commonMainImplementation", project(":core:data")) + + add("commonMainImplementation", libs.findLibrary("koin.compose").get()) + add("commonMainImplementation", libs.findLibrary("koin.compose.viewmodel").get()) + + add("commonMainImplementation", libs.findLibrary("jb.composeRuntime").get()) + add("commonMainImplementation", libs.findLibrary("jb.composeViewmodel").get()) + add("commonMainImplementation", libs.findLibrary("jb.lifecycleViewmodel").get()) + add("commonMainImplementation", libs.findLibrary("jb.lifecycleViewmodelSavedState").get()) + add("commonMainImplementation", libs.findLibrary("jb.savedstate").get()) + add("commonMainImplementation", libs.findLibrary("jb.bundle").get()) + add("commonMainImplementation", libs.findLibrary("jb.composeNavigation").get()) + add("commonMainImplementation", libs.findLibrary("kotlinx.collections.immutable").get()) + + add("androidMainImplementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) + add("androidMainImplementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + add("androidMainImplementation", libs.findLibrary("androidx.tracing.ktx").get()) + + add("androidMainImplementation", platform(libs.findLibrary("koin-bom").get())) + add("androidMainImplementation", libs.findLibrary("koin-android").get()) + add("androidMainImplementation", libs.findLibrary("koin.androidx.compose").get()) + + add("androidMainImplementation", libs.findLibrary("koin.android").get()) + add("androidMainImplementation", libs.findLibrary("koin.androidx.navigation").get()) + add("androidMainImplementation", libs.findLibrary("koin.androidx.compose").get()) + add("androidMainImplementation", libs.findLibrary("koin.core.viewmodel").get()) + + add("androidTestImplementation", libs.findLibrary("koin.test.junit4").get()) + + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.navigation.testing").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.compose.ui.test").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt new file mode 100644 index 0000000000..45253c51f2 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt @@ -0,0 +1,25 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies +import org.mifos.libs + +class KMPKoinConventionPlugin : Plugin { + + override fun apply(target: Project) { + with(target){ + with(pluginManager){ + apply("com.google.devtools.ksp") + } + + dependencies { + val bom = libs.findLibrary("koin-bom").get() + add("commonMainImplementation", platform(bom)) + add("commonMainImplementation", libs.findLibrary("koin.core").get()) + add("commonMainImplementation", libs.findLibrary("koin.annotations").get()) + add("kspCommonMainMetadata", libs.findLibrary("koin.ksp.compiler").get()) + add("commonTestImplementation", libs.findLibrary("koin.test").get()) + } + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt new file mode 100644 index 0000000000..366a18dedd --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt @@ -0,0 +1,46 @@ +import com.android.build.gradle.LibraryExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.mifos.configureFlavors +import org.mifos.configureKotlinAndroid +import org.mifos.configureKotlinMultiplatform +import org.mifos.libs + +class KMPLibraryConventionPlugin : Plugin { + + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + apply("org.jetbrains.kotlin.multiplatform") + apply("mifos.kmp.koin") + apply("mifos.detekt.plugin") + apply("mifos.spotless.plugin") + } + + configureKotlinMultiplatform() + + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = 34 + configureFlavors(this) + /** + * The resource prefix is derived from the module name, + * so resources inside ":core:module1" must be prefixed with "core_module1_" + */ + resourcePrefix = path + .split("""\W""".toRegex()) + .drop(1).distinct() + .joinToString(separator = "_") + .lowercase() + "_" + } + + dependencies { + add("commonTestImplementation", libs.findLibrary("kotlin.test").get()) + add("commonTestImplementation", libs.findLibrary("kotlinx.coroutines.test").get()) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/org/mifos/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/org/mifos/KotlinMultiplatform.kt new file mode 100644 index 0000000000..1a9da476eb --- /dev/null +++ b/build-logic/convention/src/main/kotlin/org/mifos/KotlinMultiplatform.kt @@ -0,0 +1,32 @@ +package org.mifos + +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +@OptIn(ExperimentalWasmDsl::class, ExperimentalKotlinGradlePluginApi::class) +internal fun Project.configureKotlinMultiplatform() { + extensions.configure { + applyDefaultHierarchyTemplate() + + jvm("desktop") + androidTarget() + iosSimulatorArm64() + iosX64() + iosArm64() + js(IR) { + this.nodejs() + binaries.executable() + } + wasmJs() { + browser() + nodejs() + } + + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6bea7a6f59..ae30512b3a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,4 +14,11 @@ plugins { alias(libs.plugins.detekt) apply false alias(libs.plugins.spotless) apply false alias(libs.plugins.ktlint) apply false + + //Multiplatform Plugins + alias(libs.plugins.jetbrainsCompose) apply false + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.wire) apply false + alias(libs.plugins.ktorfit) apply false } \ No newline at end of file diff --git a/compose_compiler_config.conf b/compose_compiler_config.conf new file mode 100644 index 0000000000..8a4c4ae22b --- /dev/null +++ b/compose_compiler_config.conf @@ -0,0 +1,5 @@ +// This file contains classes (with possible wildcards) that the Compose Compiler will treat as stable. +// It allows us to define classes that our not part of our codebase without wrapping them in a stable class. +// For more information, check https://developer.android.com/jetpack/compose/performance/stability/fix#configuration-file + +java.time.ZoneOffset \ No newline at end of file diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 893d00f328..b841e992f7 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -42,4 +42,4 @@ dependencies { androidTestImplementation(libs.androidx.compose.ui.test) androidTestImplementation(projects.core.testing) -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d4145fb90b..44f6e58436 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ androidDesugarJdkLibs = "2.0.4" androidIconifyMaterial = "2.2.2" androidJob = "1.2.6" androidMapsUtils = "0.4.2" -androidGradlePlugin = "8.5.0" +androidGradlePlugin = "8.5.2" androidTools = "31.6.0" androidxActivity = "1.8.2" androidxAppCompat = "1.6.1" @@ -66,7 +66,7 @@ jacoco = "0.8.7" junit4 = "4.13.2" junitJupiter = "5.8.2" junitVersion = "1.1.5" -kotlin = "1.9.22" +kotlin = "2.0.21" kotlinxCoroutines = "1.7.3" kotlinxCoroutinesCoreVersion = "1.5.1" kotlinxCoroutinesRx2 = "1.5.1" @@ -131,6 +131,14 @@ truthVersion = '1.1.5' runtimeLivedata = "1.6.8" appcompat = "1.7.0" +# Ktor & Ktorfit +ktorfit = "2.2.0" + +# CMP Libraries +compose-plugin = "1.7.0-rc01" +wire = "5.0.0" +coil-cmp = "3.0.4" + [libraries] # AndroidX Libraries accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanistDrawablepainter" } @@ -415,7 +423,6 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-kapt = { id = "kotlin-kapt", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-parcelize = { id = "kotlin-parcelize", version.ref = "kotlin" } -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } realm = { id = "io.realm.kotlin", version.ref = "realmVersion" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } @@ -424,6 +431,14 @@ ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } spotless = { id = "com.diffplug.spotless", version.ref = "spotlessVersion" } dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" } +# KMP & CMP +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +ktorfit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktorfit" } +wire = { id = "com.squareup.wire", version.ref = "wire" } + # Plugins defined by this project mifos-android-application = { id = "mifos.android.application", version = "unspecified" } mifos-android-application-flavors = { id = "mifos.android.application.flavors", version = "unspecified" } diff --git a/mifosng-android/dependencies/demoDebugCompileClasspath.txt b/mifosng-android/dependencies/demoDebugCompileClasspath.txt index b110e25982..c2c632af10 100644 --- a/mifosng-android/dependencies/demoDebugCompileClasspath.txt +++ b/mifosng-android/dependencies/demoDebugCompileClasspath.txt @@ -69,7 +69,7 @@ androidx.core:core-splashscreen:1.0.1 androidx.core:core:1.13.0 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview:1.1.0 -androidx.databinding:viewbinding:8.5.0 +androidx.databinding:viewbinding:8.5.2 androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.1.1 androidx.dynamicanimation:dynamicanimation:1.0.0 @@ -192,11 +192,11 @@ io.reactivex:rxjava:1.3.8 javax.inject:javax.inject:1 junit:junit:4.13.2 org.hamcrest:hamcrest-core:1.3 -org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -org.jetbrains.kotlin:kotlin-test:1.9.22 +org.jetbrains.kotlin:kotlin-stdlib-common:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib:2.0.21 +org.jetbrains.kotlin:kotlin-test:2.0.21 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0 diff --git a/mifosng-android/dependencies/demoReleaseCompileClasspath.txt b/mifosng-android/dependencies/demoReleaseCompileClasspath.txt index 41e6760127..c227a5a63b 100644 --- a/mifosng-android/dependencies/demoReleaseCompileClasspath.txt +++ b/mifosng-android/dependencies/demoReleaseCompileClasspath.txt @@ -64,7 +64,7 @@ androidx.core:core-splashscreen:1.0.1 androidx.core:core:1.13.0 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview:1.1.0 -androidx.databinding:viewbinding:8.5.0 +androidx.databinding:viewbinding:8.5.2 androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.1.1 androidx.dynamicanimation:dynamicanimation:1.0.0 @@ -187,11 +187,11 @@ io.reactivex:rxjava:1.3.8 javax.inject:javax.inject:1 junit:junit:4.13.2 org.hamcrest:hamcrest-core:1.3 -org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -org.jetbrains.kotlin:kotlin-test:1.9.22 +org.jetbrains.kotlin:kotlin-stdlib-common:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib:2.0.21 +org.jetbrains.kotlin:kotlin-test:2.0.21 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0 diff --git a/mifosng-android/dependencies/prodDebugCompileClasspath.txt b/mifosng-android/dependencies/prodDebugCompileClasspath.txt index b110e25982..c2c632af10 100644 --- a/mifosng-android/dependencies/prodDebugCompileClasspath.txt +++ b/mifosng-android/dependencies/prodDebugCompileClasspath.txt @@ -69,7 +69,7 @@ androidx.core:core-splashscreen:1.0.1 androidx.core:core:1.13.0 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview:1.1.0 -androidx.databinding:viewbinding:8.5.0 +androidx.databinding:viewbinding:8.5.2 androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.1.1 androidx.dynamicanimation:dynamicanimation:1.0.0 @@ -192,11 +192,11 @@ io.reactivex:rxjava:1.3.8 javax.inject:javax.inject:1 junit:junit:4.13.2 org.hamcrest:hamcrest-core:1.3 -org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -org.jetbrains.kotlin:kotlin-test:1.9.22 +org.jetbrains.kotlin:kotlin-stdlib-common:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib:2.0.21 +org.jetbrains.kotlin:kotlin-test:2.0.21 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0 diff --git a/mifosng-android/dependencies/prodReleaseCompileClasspath.txt b/mifosng-android/dependencies/prodReleaseCompileClasspath.txt index 41e6760127..c227a5a63b 100644 --- a/mifosng-android/dependencies/prodReleaseCompileClasspath.txt +++ b/mifosng-android/dependencies/prodReleaseCompileClasspath.txt @@ -64,7 +64,7 @@ androidx.core:core-splashscreen:1.0.1 androidx.core:core:1.13.0 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview:1.1.0 -androidx.databinding:viewbinding:8.5.0 +androidx.databinding:viewbinding:8.5.2 androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.1.1 androidx.dynamicanimation:dynamicanimation:1.0.0 @@ -187,11 +187,11 @@ io.reactivex:rxjava:1.3.8 javax.inject:javax.inject:1 junit:junit:4.13.2 org.hamcrest:hamcrest-core:1.3 -org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0 -org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -org.jetbrains.kotlin:kotlin-test:1.9.22 +org.jetbrains.kotlin:kotlin-stdlib-common:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21 +org.jetbrains.kotlin:kotlin-stdlib:2.0.21 +org.jetbrains.kotlin:kotlin-test:2.0.21 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0 diff --git a/mifosng-android/prodRelease-badging.txt b/mifosng-android/prodRelease-badging.txt new file mode 100644 index 0000000000..2a9da4b16c --- /dev/null +++ b/mifosng-android/prodRelease-badging.txt @@ -0,0 +1,134 @@ +package: name='com.mifos.mifosxdroid' versionCode='6' versionName='1.0.1' platformBuildVersionName='UpsideDownCake' platformBuildVersionCode='33' compileSdkVersion='33' compileSdkVersionCodename='UpsideDownCake' +sdkVersion:'26' +targetSdkVersion:'34' +uses-permission: name='android.permission.INTERNET' +uses-permission: name='android.permission.CAMERA' +uses-permission: name='android.permission.READ_EXTERNAL_STORAGE' +uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' maxSdkVersion='28' +uses-permission: name='android.permission.MANAGE_EXTERNAL_STORAGE' +uses-permission: name='android.permission.READ_MEDIA_IMAGES' +uses-permission: name='android.permission.VIBRATE' +uses-permission: name='android.permission.ACCESS_NETWORK_STATE' +uses-permission: name='android.permission.REORDER_TASKS' +uses-permission: name='android.permission.WAKE_LOCK' +uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' +uses-permission: name='com.mifos.mifosxdroid.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION' +application-label:'MifosXDroid' +application-label-af:'MifosXDroid' +application-label-am:'MifosXDroid' +application-label-ar:'MifosXDroid' +application-label-as:'MifosXDroid' +application-label-az:'MifosXDroid' +application-label-be:'MifosXDroid' +application-label-bg:'MifosXDroid' +application-label-bn:'MifosXDroid' +application-label-bs:'MifosXDroid' +application-label-ca:'MifosXDroid' +application-label-cs:'MifosXDroid' +application-label-da:'MifosXDroid' +application-label-de:'MifosXDroid' +application-label-el:'MifosXDroid' +application-label-en:'MifosXDroid' +application-label-en-AU:'MifosXDroid' +application-label-en-CA:'MifosXDroid' +application-label-en-GB:'MifosXDroid' +application-label-en-IN:'MifosXDroid' +application-label-en-XC:'MifosXDroid' +application-label-es:'MifosXDroid' +application-label-es-419:'MifosXDroid' +application-label-es-US:'MifosXDroid' +application-label-et:'MifosXDroid' +application-label-eu:'MifosXDroid' +application-label-fa:'MifosXDroid' +application-label-fi:'MifosXDroid' +application-label-fr:'MifosXDroid' +application-label-fr-CA:'MifosXDroid' +application-label-gl:'MifosXDroid' +application-label-gu:'MifosXDroid' +application-label-hi:'MifosXDroid' +application-label-hr:'MifosXDroid' +application-label-hu:'MifosXDroid' +application-label-hy:'MifosXDroid' +application-label-in:'MifosXDroid' +application-label-is:'MifosXDroid' +application-label-it:'MifosXDroid' +application-label-iw:'MifosXDroid' +application-label-ja:'MifosXDroid' +application-label-ka:'MifosXDroid' +application-label-kk:'MifosXDroid' +application-label-km:'MifosXDroid' +application-label-kn:'MifosXDroid' +application-label-ko:'MifosXDroid' +application-label-ky:'MifosXDroid' +application-label-lo:'MifosXDroid' +application-label-lt:'MifosXDroid' +application-label-lv:'MifosXDroid' +application-label-mk:'MifosXDroid' +application-label-ml:'MifosXDroid' +application-label-mn:'MifosXDroid' +application-label-mr:'MifosXDroid' +application-label-ms:'MifosXDroid' +application-label-my:'MifosXDroid' +application-label-nb:'MifosXDroid' +application-label-ne:'MifosXDroid' +application-label-nl:'MifosXDroid' +application-label-or:'MifosXDroid' +application-label-pa:'MifosXDroid' +application-label-pl:'MifosXDroid' +application-label-pt:'MifosXDroid' +application-label-pt-BR:'MifosXDroid' +application-label-pt-PT:'MifosXDroid' +application-label-ro:'MifosXDroid' +application-label-ru:'MifosXDroid' +application-label-si:'MifosXDroid' +application-label-sk:'MifosXDroid' +application-label-sl:'MifosXDroid' +application-label-sq:'MifosXDroid' +application-label-sr:'MifosXDroid' +application-label-sr-Latn:'MifosXDroid' +application-label-sv:'MifosXDroid' +application-label-sw:'MifosXDroid' +application-label-ta:'MifosXDroid' +application-label-te:'MifosXDroid' +application-label-th:'MifosXDroid' +application-label-tl:'MifosXDroid' +application-label-tr:'MifosXDroid' +application-label-uk:'MifosXDroid' +application-label-ur:'MifosXDroid' +application-label-uz:'MifosXDroid' +application-label-vi:'MifosXDroid' +application-label-zh:'MifosXDroid' +application-label-zh-CN:'MifosXDroid' +application-label-zh-HK:'MifosXDroid' +application-label-zh-TW:'MifosXDroid' +application-label-zu:'MifosXDroid' +application-icon-120:'res/drawable/feature_client_ic_launcher.png' +application-icon-160:'res/drawable/feature_client_ic_launcher.png' +application-icon-240:'res/drawable/feature_client_ic_launcher.png' +application-icon-320:'res/drawable/feature_client_ic_launcher.png' +application-icon-480:'res/drawable/feature_client_ic_launcher.png' +application-icon-640:'res/drawable/feature_client_ic_launcher.png' +application-icon-65534:'res/drawable/feature_client_ic_launcher.png' +application: label='MifosXDroid' icon='res/drawable/feature_client_ic_launcher.png' +testOnly='-1' +launchable-activity: name='com.mifos.mifosxdroid.AndroidClientActivity' label='' icon='' +uses-library-not-required:'org.apache.http.legacy' +uses-library-not-required:'androidx.window.extensions' +uses-library-not-required:'androidx.window.sidecar' +feature-group: label='' + uses-gl-es: '0x20000' + uses-feature: name='android.hardware.camera.any' + uses-feature: name='android.hardware.camera' + uses-implied-feature: name='android.hardware.camera' reason='requested android.permission.CAMERA permission' + uses-feature: name='android.hardware.faketouch' + uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps' + uses-feature: name='android.hardware.screen.portrait' + uses-implied-feature: name='android.hardware.screen.portrait' reason='one or more activities have specified a portrait orientation' +main +other-activities +other-receivers +other-services +supports-screens: 'small' 'normal' 'large' 'xlarge' +supports-any-density: 'true' +locales: '--_--' 'af' 'am' 'ar' 'as' 'az' 'be' 'bg' 'bn' 'bs' 'ca' 'cs' 'da' 'de' 'el' 'en' 'en-AU' 'en-CA' 'en-GB' 'en-IN' 'en-XC' 'es' 'es-419' 'es-US' 'et' 'eu' 'fa' 'fi' 'fr' 'fr-CA' 'gl' 'gu' 'hi' 'hr' 'hu' 'hy' 'in' 'is' 'it' 'iw' 'ja' 'ka' 'kk' 'km' 'kn' 'ko' 'ky' 'lo' 'lt' 'lv' 'mk' 'ml' 'mn' 'mr' 'ms' 'my' 'nb' 'ne' 'nl' 'or' 'pa' 'pl' 'pt' 'pt-BR' 'pt-PT' 'ro' 'ru' 'si' 'sk' 'sl' 'sq' 'sr' 'sr-Latn' 'sv' 'sw' 'ta' 'te' 'th' 'tl' 'tr' 'uk' 'ur' 'uz' 'vi' 'zh' 'zh-CN' 'zh-HK' 'zh-TW' 'zu' +densities: '120' '160' '240' '320' '480' '640' '65534'