Remove viewmodel/fragment references
This commit is contained in:
parent
7b197e6e8b
commit
3abba8e88f
21 changed files with 0 additions and 544 deletions
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.x.anvilannotations
|
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds view model to the specified component graph.
|
|
||||||
* Equivalent to the following declaration in a dagger module:
|
|
||||||
*
|
|
||||||
* @Binds
|
|
||||||
* @IntoMap
|
|
||||||
* @ViewModelKey(YourViewModel::class)
|
|
||||||
* public abstract fun bindYourViewModelFactory(factory: YourViewModel.Factory): AssistedViewModelFactory<*, *>
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class ContributesViewModel(
|
|
||||||
val scope: KClass<*>,
|
|
||||||
)
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:OptIn(ExperimentalAnvilApi::class)
|
|
||||||
|
|
||||||
package io.element.android.x.anvilcodegen
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService
|
|
||||||
import com.squareup.anvil.annotations.ContributesTo
|
|
||||||
import com.squareup.anvil.annotations.ExperimentalAnvilApi
|
|
||||||
import com.squareup.anvil.compiler.api.AnvilCompilationException
|
|
||||||
import com.squareup.anvil.compiler.api.AnvilContext
|
|
||||||
import com.squareup.anvil.compiler.api.CodeGenerator
|
|
||||||
import com.squareup.anvil.compiler.api.GeneratedFile
|
|
||||||
import com.squareup.anvil.compiler.api.createGeneratedFile
|
|
||||||
import com.squareup.anvil.compiler.internal.asClassName
|
|
||||||
import com.squareup.anvil.compiler.internal.buildFile
|
|
||||||
import com.squareup.anvil.compiler.internal.fqName
|
|
||||||
import com.squareup.anvil.compiler.internal.reference.ClassReference
|
|
||||||
import com.squareup.anvil.compiler.internal.reference.asClassName
|
|
||||||
import com.squareup.anvil.compiler.internal.reference.classAndInnerClassReferences
|
|
||||||
import com.squareup.kotlinpoet.AnnotationSpec
|
|
||||||
import com.squareup.kotlinpoet.ClassName
|
|
||||||
import com.squareup.kotlinpoet.FileSpec
|
|
||||||
import com.squareup.kotlinpoet.FunSpec
|
|
||||||
import com.squareup.kotlinpoet.KModifier
|
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
|
||||||
import com.squareup.kotlinpoet.STAR
|
|
||||||
import com.squareup.kotlinpoet.TypeSpec
|
|
||||||
import dagger.Binds
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.assisted.Assisted
|
|
||||||
import dagger.assisted.AssistedFactory
|
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import dagger.multibindings.IntoMap
|
|
||||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
|
||||||
import java.io.File
|
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an anvil plugin that allows ViewModels to use [ContributesViewModel] alone and let this plugin automatically
|
|
||||||
* handle the rest of the Dagger wiring required for constructor injection.
|
|
||||||
*/
|
|
||||||
@AutoService(CodeGenerator::class)
|
|
||||||
class ContributesViewModelCodeGenerator : CodeGenerator {
|
|
||||||
|
|
||||||
override fun isApplicable(context: AnvilContext): Boolean = true
|
|
||||||
|
|
||||||
override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection<KtFile>): Collection<GeneratedFile> {
|
|
||||||
return projectFiles.classAndInnerClassReferences(module)
|
|
||||||
.filter { it.isAnnotatedWith(ContributesViewModel::class.fqName) }
|
|
||||||
.flatMap { listOf(generateModule(it, codeGenDir, module), generateAssistedFactory(it, codeGenDir, module)) }
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile {
|
|
||||||
val generatedPackage = vmClass.packageFqName.toString()
|
|
||||||
val moduleClassName = "${vmClass.shortName}_Module"
|
|
||||||
val scope = vmClass.annotations.single { it.fqName == ContributesViewModel::class.fqName }.scope()
|
|
||||||
val content = FileSpec.buildFile(generatedPackage, moduleClassName) {
|
|
||||||
addType(
|
|
||||||
TypeSpec.classBuilder(moduleClassName)
|
|
||||||
.addModifiers(KModifier.ABSTRACT)
|
|
||||||
.addAnnotation(Module::class)
|
|
||||||
.addAnnotation(AnnotationSpec.builder(ContributesTo::class).addMember("%T::class", scope.asClassName()).build())
|
|
||||||
.addFunction(
|
|
||||||
FunSpec.builder("bind${vmClass.shortName}Factory")
|
|
||||||
.addModifiers(KModifier.ABSTRACT)
|
|
||||||
.addParameter("factory", ClassName(generatedPackage, "${vmClass.shortName}_AssistedFactory"))
|
|
||||||
.returns(assistedViewModelFactoryFqName.asClassName(module).parameterizedBy(STAR, STAR))
|
|
||||||
.addAnnotation(Binds::class)
|
|
||||||
.addAnnotation(IntoMap::class)
|
|
||||||
.addAnnotation(
|
|
||||||
AnnotationSpec.Companion
|
|
||||||
.builder(viewModelKeyFqName.asClassName(module))
|
|
||||||
.addMember("%T::class", vmClass.asClassName())
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateAssistedFactory(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile {
|
|
||||||
val generatedPackage = vmClass.packageFqName.toString()
|
|
||||||
val assistedFactoryClassName = "${vmClass.shortName}_AssistedFactory"
|
|
||||||
val constructor = vmClass.constructors.singleOrNull { it.isAnnotatedWith(AssistedInject::class.fqName) }
|
|
||||||
val assistedParameter = constructor?.parameters?.singleOrNull { it.isAnnotatedWith(Assisted::class.fqName) }
|
|
||||||
if (constructor == null || assistedParameter == null) {
|
|
||||||
throw AnvilCompilationException(
|
|
||||||
"${vmClass.fqName} must have an @AssistedInject constructor with @Assisted initialState: S parameter",
|
|
||||||
element = vmClass.clazz,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (assistedParameter.name != "initialState") {
|
|
||||||
throw AnvilCompilationException(
|
|
||||||
"${vmClass.fqName} @Assisted parameter must be named initialState",
|
|
||||||
element = assistedParameter.parameter,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val vmClassName = vmClass.asClassName()
|
|
||||||
val stateClassName = assistedParameter.type().asTypeName()
|
|
||||||
val content = FileSpec.buildFile(generatedPackage, assistedFactoryClassName) {
|
|
||||||
addType(
|
|
||||||
TypeSpec.interfaceBuilder(assistedFactoryClassName)
|
|
||||||
.addSuperinterface(assistedViewModelFactoryFqName.asClassName(module).parameterizedBy(vmClassName, stateClassName))
|
|
||||||
.addAnnotation(AssistedFactory::class)
|
|
||||||
.addFunction(
|
|
||||||
FunSpec.builder("create")
|
|
||||||
.addModifiers(KModifier.OVERRIDE, KModifier.ABSTRACT)
|
|
||||||
.addParameter("initialState", stateClassName)
|
|
||||||
.returns(vmClassName)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return createGeneratedFile(codeGenDir, generatedPackage, assistedFactoryClassName, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val assistedViewModelFactoryFqName = FqName("io.element.android.x.architecture.viewmodel.AssistedViewModelFactory")
|
|
||||||
private val viewModelKeyFqName = FqName("io.element.android.x.architecture.viewmodel.ViewModelKey")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -175,11 +175,9 @@ dependencies {
|
||||||
|
|
||||||
implementation(libs.androidx.corektx)
|
implementation(libs.androidx.corektx)
|
||||||
implementation(libs.androidx.lifecycle.runtime)
|
implementation(libs.androidx.lifecycle.runtime)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.androidx.startup)
|
implementation(libs.androidx.startup)
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
implementation(libs.mavericks.compose)
|
|
||||||
|
|
||||||
implementation(libs.dagger)
|
implementation(libs.dagger)
|
||||||
kapt(libs.dagger.compiler)
|
kapt(libs.dagger.compiler)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import io.element.android.x.di.AppComponent
|
||||||
import io.element.android.x.di.DaggerAppComponent
|
import io.element.android.x.di.DaggerAppComponent
|
||||||
import io.element.android.x.initializer.CrashInitializer
|
import io.element.android.x.initializer.CrashInitializer
|
||||||
import io.element.android.x.initializer.MatrixInitializer
|
import io.element.android.x.initializer.MatrixInitializer
|
||||||
import io.element.android.x.initializer.MavericksInitializer
|
|
||||||
import io.element.android.x.initializer.TimberInitializer
|
import io.element.android.x.initializer.TimberInitializer
|
||||||
|
|
||||||
class ElementXApplication : Application(), DaggerComponentOwner {
|
class ElementXApplication : Application(), DaggerComponentOwner {
|
||||||
|
|
@ -40,7 +39,6 @@ class ElementXApplication : Application(), DaggerComponentOwner {
|
||||||
initializeComponent(CrashInitializer::class.java)
|
initializeComponent(CrashInitializer::class.java)
|
||||||
initializeComponent(TimberInitializer::class.java)
|
initializeComponent(TimberInitializer::class.java)
|
||||||
initializeComponent(MatrixInitializer::class.java)
|
initializeComponent(MatrixInitializer::class.java)
|
||||||
initializeComponent(MavericksInitializer::class.java)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import com.squareup.anvil.annotations.MergeComponent
|
||||||
import dagger.BindsInstance
|
import dagger.BindsInstance
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
import io.element.android.x.architecture.NodeFactoriesBindings
|
import io.element.android.x.architecture.NodeFactoriesBindings
|
||||||
import io.element.android.x.architecture.viewmodel.DaggerMavericksBindings
|
|
||||||
|
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
@MergeComponent(AppScope::class)
|
@MergeComponent(AppScope::class)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import com.squareup.anvil.annotations.ContributesTo
|
||||||
import com.squareup.anvil.annotations.MergeSubcomponent
|
import com.squareup.anvil.annotations.MergeSubcomponent
|
||||||
import dagger.BindsInstance
|
import dagger.BindsInstance
|
||||||
import dagger.Subcomponent
|
import dagger.Subcomponent
|
||||||
import io.element.android.x.architecture.viewmodel.DaggerMavericksBindings
|
|
||||||
import io.element.android.x.architecture.NodeFactoriesBindings
|
import io.element.android.x.architecture.NodeFactoriesBindings
|
||||||
import io.element.android.x.matrix.MatrixClient
|
import io.element.android.x.matrix.MatrixClient
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.x.initializer
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.startup.Initializer
|
|
||||||
import com.airbnb.mvrx.Mavericks
|
|
||||||
|
|
||||||
class MavericksInitializer : Initializer<Unit> {
|
|
||||||
|
|
||||||
override fun create(context: Context) {
|
|
||||||
Mavericks.initialize(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dependencies(): List<Class<out Initializer<*>>> = listOf()
|
|
||||||
}
|
|
||||||
|
|
@ -41,7 +41,6 @@ dependencies {
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:elementresources"))
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(libs.appyx.core)
|
implementation(libs.appyx.core)
|
||||||
implementation(libs.mavericks.compose)
|
|
||||||
ksp(libs.showkase.processor)
|
ksp(libs.showkase.processor)
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
androidTestImplementation(libs.test.junitext)
|
androidTestImplementation(libs.test.junitext)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ dependencies {
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:textcomposer"))
|
implementation(project(":libraries:textcomposer"))
|
||||||
implementation(libs.appyx.core)
|
implementation(libs.appyx.core)
|
||||||
implementation(libs.mavericks.compose)
|
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
implementation(libs.datetime)
|
implementation(libs.datetime)
|
||||||
implementation(libs.accompanist.flowlayout)
|
implementation(libs.accompanist.flowlayout)
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ dependencies {
|
||||||
implementation(project(":libraries:elementresources"))
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:architecture"))
|
implementation(project(":libraries:architecture"))
|
||||||
implementation(libs.mavericks.compose)
|
|
||||||
implementation(libs.accompanist.pager)
|
implementation(libs.accompanist.pager)
|
||||||
implementation(libs.accompanist.pagerindicator)
|
implementation(libs.accompanist.pagerindicator)
|
||||||
implementation(libs.appyx.core)
|
implementation(libs.appyx.core)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ dependencies {
|
||||||
implementation(project(":features:logout"))
|
implementation(project(":features:logout"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:elementresources"))
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(libs.mavericks.compose)
|
|
||||||
implementation(libs.datetime)
|
implementation(libs.datetime)
|
||||||
implementation(libs.accompanist.placeholder)
|
implementation(libs.accompanist.placeholder)
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ dependencies {
|
||||||
implementation(project(":anvilannotations"))
|
implementation(project(":anvilannotations"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:elementresources"))
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(libs.mavericks.compose)
|
|
||||||
implementation(libs.squareup.seismic)
|
implementation(libs.squareup.seismic)
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ constraintlayout = "2.1.4"
|
||||||
recyclerview = "1.2.1"
|
recyclerview = "1.2.1"
|
||||||
lifecycle = "2.5.1"
|
lifecycle = "2.5.1"
|
||||||
activity_compose = "1.6.1"
|
activity_compose = "1.6.1"
|
||||||
fragment = "1.5.5"
|
|
||||||
startup = "1.1.1"
|
startup = "1.1.1"
|
||||||
|
|
||||||
# Compose
|
# Compose
|
||||||
|
|
@ -42,7 +41,6 @@ test_hamcrest = "2.2"
|
||||||
test_orchestrator = "1.4.1"
|
test_orchestrator = "1.4.1"
|
||||||
|
|
||||||
#other
|
#other
|
||||||
mavericks = "3.0.1"
|
|
||||||
coil = "2.2.1"
|
coil = "2.2.1"
|
||||||
datetime = "0.4.0"
|
datetime = "0.4.0"
|
||||||
wysiwyg = "0.7.0.1"
|
wysiwyg = "0.7.0.1"
|
||||||
|
|
@ -79,9 +77,7 @@ androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-kt
|
||||||
androidx_lifecycle_compose = { module = "androidx.lifecycle:compose", version.ref = "lifecycle" }
|
androidx_lifecycle_compose = { module = "androidx.lifecycle:compose", version.ref = "lifecycle" }
|
||||||
androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" }
|
androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" }
|
||||||
|
|
||||||
androidx_lifecycle_viewmodel_compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
|
||||||
androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity_compose" }
|
androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity_compose" }
|
||||||
androidx_fragment = { module = "androidx.fragment:fragment-ktx", version.ref = "fragment" }
|
|
||||||
androidx_startup = { module = "androidx.startup:startup-runtime", version.ref = "startup" }
|
androidx_startup = { module = "androidx.startup:startup-runtime", version.ref = "startup" }
|
||||||
|
|
||||||
androidx_compose_bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" }
|
androidx_compose_bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" }
|
||||||
|
|
@ -117,7 +113,6 @@ test_hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "test_hamcrest
|
||||||
test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test_orchestrator" }
|
test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test_orchestrator" }
|
||||||
|
|
||||||
# Others
|
# Others
|
||||||
mavericks_compose = { module = "com.airbnb.android:mavericks-compose", version.ref = "mavericks" }
|
|
||||||
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
||||||
coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
|
coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
|
||||||
datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,4 @@ dependencies {
|
||||||
api(libs.dagger)
|
api(libs.dagger)
|
||||||
api(libs.appyx.core)
|
api(libs.appyx.core)
|
||||||
api(libs.androidx.lifecycle.runtime)
|
api(libs.androidx.lifecycle.runtime)
|
||||||
api(libs.mavericks.compose)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.x.architecture.viewmodel
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
|
||||||
|
|
||||||
interface AssistedViewModelFactory<VM : MavericksViewModel<S>, S : MavericksState> {
|
|
||||||
fun create(initialState: S): VM
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.element.android.x.architecture.viewmodel
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import io.element.android.x.architecture.bindings
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To connect Mavericks ViewModel creation with Anvil's dependency injection, add the following to your MavericksViewModel.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
*
|
|
||||||
* @ContributesViewModel(YourScope::class)
|
|
||||||
* class MyViewModel @AssistedInject constructor(
|
|
||||||
* @Assisted initialState: MyState,
|
|
||||||
* …,
|
|
||||||
* ): MavericksViewModel<MyState>(...) {
|
|
||||||
* …
|
|
||||||
*
|
|
||||||
* companion object : MavericksViewModelFactory<MyViewModel, MyState> by daggerMavericksViewModelFactory()
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline fun <reified VM : MavericksViewModel<S>, S : MavericksState> daggerMavericksViewModelFactory() = DaggerMavericksViewModelFactory(VM::class.java)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [MavericksViewModelFactory] makes it easy to create instances of a ViewModel
|
|
||||||
* using its AssistedInject Factory. This class should be implemented by the companion object
|
|
||||||
* of every ViewModel which uses AssistedInject via [daggerMavericksViewModelFactory].
|
|
||||||
*
|
|
||||||
* @param VM The ViewModel type
|
|
||||||
* @param S The ViewState type
|
|
||||||
* @param viewModelClass The [Class] of the ViewModel being requested for creation
|
|
||||||
*
|
|
||||||
* This class accesses the map of ViewModel class to [AssistedViewModelFactory]s from the nearest [DaggerComponentOwner] and
|
|
||||||
* uses it to retrieve the requested ViewModel's factory class. It then creates an instance of this ViewModel
|
|
||||||
* using the retrieved factory and returns it.
|
|
||||||
* @see daggerMavericksViewModelFactory
|
|
||||||
*/
|
|
||||||
class DaggerMavericksViewModelFactory<VM : MavericksViewModel<S>, S : MavericksState>(
|
|
||||||
private val viewModelClass: Class<VM>
|
|
||||||
) : MavericksViewModelFactory<VM, S> {
|
|
||||||
|
|
||||||
override fun create(viewModelContext: ViewModelContext, state: S): VM {
|
|
||||||
val bindings: DaggerMavericksBindings = viewModelContext.activity.bindings()
|
|
||||||
val viewModelFactoryMap = bindings.viewModelFactories()
|
|
||||||
val viewModelFactory = viewModelFactoryMap[viewModelClass] ?: error("Cannot find ViewModelFactory for ${viewModelClass.name}.")
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val castedViewModelFactory = viewModelFactory as? AssistedViewModelFactory<VM, S>
|
|
||||||
val viewModel = castedViewModelFactory?.create(state)
|
|
||||||
return viewModel as VM
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DaggerMavericksBindings {
|
|
||||||
fun viewModelFactories(): Map<Class<out MavericksViewModel<*>>, AssistedViewModelFactory<*, *>>
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.x.architecture.viewmodel
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
|
||||||
import dagger.MapKey
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
|
||||||
@MapKey
|
|
||||||
annotation class ViewModelKey(val value: KClass<out MavericksViewModel<*>>)
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
package io.element.android.x.architecture.viewmodel
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.lifecycle.DEFAULT_ARGS_KEY
|
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
|
||||||
import androidx.lifecycle.HasDefaultViewModelProviderFactory
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.SAVED_STATE_REGISTRY_OWNER_KEY
|
|
||||||
import androidx.lifecycle.SavedStateViewModelFactory
|
|
||||||
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.ViewModelStore
|
|
||||||
import androidx.lifecycle.ViewModelStoreOwner
|
|
||||||
import androidx.lifecycle.enableSavedStateHandles
|
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
|
||||||
import androidx.lifecycle.viewmodel.MutableCreationExtras
|
|
||||||
import androidx.savedstate.SavedStateRegistry
|
|
||||||
import androidx.savedstate.SavedStateRegistryController
|
|
||||||
import androidx.savedstate.SavedStateRegistryOwner
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
|
||||||
import com.bumble.appyx.core.node.Node
|
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
|
||||||
|
|
||||||
fun viewModelSupportNode(buildContext: BuildContext, plugins: List<Plugin> = emptyList(), composable: @Composable (Modifier) -> Unit): Node =
|
|
||||||
ViewModelSupportNode(buildContext, plugins, composable)
|
|
||||||
|
|
||||||
class ViewModelSupportNode(
|
|
||||||
buildContext: BuildContext,
|
|
||||||
plugins: List<Plugin> = emptyList(),
|
|
||||||
private val composable: @Composable (Modifier) -> Unit,
|
|
||||||
) : Node(
|
|
||||||
buildContext, plugins = plugins
|
|
||||||
), ViewModelStoreOwner, SavedStateRegistryOwner {
|
|
||||||
|
|
||||||
private val viewModelSupport = ViewModelSupport(
|
|
||||||
lifecycle,
|
|
||||||
buildContext.savedStateMap?.get("SAVED_STATE_REGISTRY") as Bundle?,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getViewModelStore(): ViewModelStore {
|
|
||||||
return viewModelSupport.viewModelStore
|
|
||||||
}
|
|
||||||
|
|
||||||
override val savedStateRegistry: SavedStateRegistry
|
|
||||||
get() = viewModelSupport.savedStateRegistry
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun View(modifier: Modifier) {
|
|
||||||
composable(modifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ViewModelSupport(
|
|
||||||
private val lifecycle: Lifecycle,
|
|
||||||
private val initialSavedState: Bundle?,
|
|
||||||
val defaultArgs: Bundle? = null,
|
|
||||||
) : ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner {
|
|
||||||
|
|
||||||
private val viewModelStore = ViewModelStore()
|
|
||||||
private val savedStateRegistryController: SavedStateRegistryController =
|
|
||||||
SavedStateRegistryController.create(this)
|
|
||||||
|
|
||||||
//Don't replace the initial saved state until we have at least started
|
|
||||||
private var canSaveState: Boolean = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
savedStateRegistryController.performAttach()
|
|
||||||
|
|
||||||
// We copy the bundle because the `savedStateRegistryController` will modify it.
|
|
||||||
// We don't want to modify `initialSavedState` since we may need to return that as our
|
|
||||||
// state in `saveState`.
|
|
||||||
savedStateRegistryController.performRestore(initialSavedState?.let { Bundle(it) })
|
|
||||||
enableSavedStateHandles()
|
|
||||||
|
|
||||||
lifecycle.addObserver(object : DefaultLifecycleObserver {
|
|
||||||
override fun onStart(owner: LifecycleOwner) {
|
|
||||||
canSaveState = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy(owner: LifecycleOwner) {
|
|
||||||
viewModelStore.clear()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getViewModelStore(): ViewModelStore {
|
|
||||||
return viewModelStore
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory {
|
|
||||||
return SavedStateViewModelFactory(null, this, defaultArgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDefaultViewModelCreationExtras(): CreationExtras {
|
|
||||||
val extras = MutableCreationExtras()
|
|
||||||
extras[SAVED_STATE_REGISTRY_OWNER_KEY] = this
|
|
||||||
extras[VIEW_MODEL_STORE_OWNER_KEY] = this
|
|
||||||
defaultArgs?.let { args ->
|
|
||||||
extras[DEFAULT_ARGS_KEY] = args
|
|
||||||
}
|
|
||||||
return extras
|
|
||||||
}
|
|
||||||
|
|
||||||
override val savedStateRegistry: SavedStateRegistry
|
|
||||||
get() = savedStateRegistryController.savedStateRegistry
|
|
||||||
|
|
||||||
override fun getLifecycle(): Lifecycle {
|
|
||||||
return lifecycle
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveState(): Bundle? {
|
|
||||||
return if (canSaveState) {
|
|
||||||
Bundle().also(savedStateRegistryController::performSave)
|
|
||||||
} else {
|
|
||||||
initialSavedState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.x.matrix.ui.viewmodels.user
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
|
||||||
import dagger.assisted.Assisted
|
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
|
||||||
import io.element.android.x.architecture.viewmodel.daggerMavericksViewModelFactory
|
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
|
||||||
import io.element.android.x.di.SessionScope
|
|
||||||
import io.element.android.x.matrix.MatrixClient
|
|
||||||
import io.element.android.x.matrix.ui.MatrixItemHelper
|
|
||||||
|
|
||||||
@ContributesViewModel(SessionScope::class)
|
|
||||||
class UserViewModel @AssistedInject constructor(
|
|
||||||
client: MatrixClient,
|
|
||||||
@Assisted initialState: UserViewState
|
|
||||||
) : MavericksViewModel<UserViewState>(initialState) {
|
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<UserViewModel, UserViewState> by daggerMavericksViewModelFactory()
|
|
||||||
|
|
||||||
private val matrixUserHelper = MatrixItemHelper(client)
|
|
||||||
|
|
||||||
init {
|
|
||||||
handleInit()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleInit() {
|
|
||||||
matrixUserHelper.getCurrentUserData(avatarSize = AvatarSize.SMALL).execute {
|
|
||||||
copy(user = it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.x.matrix.ui.viewmodels.user
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.Async
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
|
||||||
import com.airbnb.mvrx.Uninitialized
|
|
||||||
import io.element.android.x.matrix.ui.model.MatrixUser
|
|
||||||
|
|
||||||
data class UserViewState(
|
|
||||||
val user: Async<MatrixUser> = Uninitialized,
|
|
||||||
) : MavericksState
|
|
||||||
|
|
@ -43,7 +43,6 @@ fun DependencyHandlerScope.composeDependencies() {
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
|
||||||
implementation("androidx.activity:activity-compose:1.6.1")
|
implementation("androidx.activity:activity-compose:1.6.1")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
|
||||||
implementation("com.airbnb.android:mavericks-compose:3.0.1")
|
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||||
implementation("com.airbnb.android:showkase:1.0.0-beta14")
|
implementation("com.airbnb.android:showkase:1.0.0-beta14")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue