├── sample ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values-land │ │ │ │ └── dimens.xml │ │ │ ├── values-w1240dp │ │ │ │ └── dimens.xml │ │ │ ├── values-w600dp │ │ │ │ └── dimens.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── xml │ │ │ │ └── network_security_config.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── themes.xml │ │ │ │ └── strings.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable │ │ │ │ ├── ic_home_black_24dp.xml │ │ │ │ ├── ic_dashboard_black_24dp.xml │ │ │ │ ├── ic_notifications_black_24dp.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v33 │ │ │ │ └── ic_launcher.xml │ │ │ ├── menu │ │ │ │ └── bottom_nav_menu.xml │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ ├── layout │ │ │ │ ├── fragment_home.xml │ │ │ │ ├── fragment_dashboard.xml │ │ │ │ ├── fragment_notifications.xml │ │ │ │ ├── dialog_loading.xml │ │ │ │ ├── activity_bottom_navigation_acitvity.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_net_work.xml │ │ │ │ ├── activity_login.xml │ │ │ │ └── activity_detail.xml │ │ │ ├── navigation │ │ │ │ └── mobile_navigation.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── layout-w1240dp │ │ │ │ └── activity_login.xml │ │ │ └── layout-w936dp │ │ │ │ └── activity_login.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── fastmvvm │ │ │ │ └── sample │ │ │ │ ├── repository │ │ │ │ └── MainRepository.kt │ │ │ │ ├── view │ │ │ │ └── activity │ │ │ │ │ ├── ui │ │ │ │ │ ├── login │ │ │ │ │ │ ├── LoginResult.kt │ │ │ │ │ │ ├── LoggedInUserView.kt │ │ │ │ │ │ ├── LoginFormState.kt │ │ │ │ │ │ ├── LoginViewModelFactory.kt │ │ │ │ │ │ └── LoginViewModel.kt │ │ │ │ │ ├── home │ │ │ │ │ │ ├── HomeViewModel.kt │ │ │ │ │ │ └── HomeFragment.kt │ │ │ │ │ ├── dashboard │ │ │ │ │ │ ├── DashboardViewModel.kt │ │ │ │ │ │ └── DashboardFragment.kt │ │ │ │ │ └── notifications │ │ │ │ │ │ ├── NotificationsViewModel.kt │ │ │ │ │ │ └── NotificationsFragment.kt │ │ │ │ │ ├── data │ │ │ │ │ ├── model │ │ │ │ │ │ └── LoggedInUser.kt │ │ │ │ │ ├── Result.kt │ │ │ │ │ ├── LoginDataSource.kt │ │ │ │ │ └── LoginRepository.kt │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ ├── BottomNavigationAcitvity.kt │ │ │ │ │ ├── DetailActivity.kt │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── NetworkActivity.kt │ │ │ │ │ └── LoginActivity.kt │ │ │ │ ├── network │ │ │ │ ├── ApiResponse.kt │ │ │ │ ├── Api.kt │ │ │ │ ├── Banner.kt │ │ │ │ ├── NetworkClient.kt │ │ │ │ ├── LoginInfo.kt │ │ │ │ └── User.kt │ │ │ │ └── App.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── fastmvvm │ │ │ └── sample │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── fastmvvm │ │ └── sample │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── easyAndroid ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── yeqiu │ │ │ └── easyandroid │ │ │ ├── Common.kt │ │ │ ├── ActivityStarter.kt │ │ │ ├── AppInfo.kt │ │ │ └── Logger.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── yeqiu │ │ │ └── easyandroid │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── yeqiu │ │ └── easyandroid │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── fastMvvm ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── yeqiu │ │ │ └── fastmvvm │ │ │ ├── view │ │ │ ├── BaseFragment.kt │ │ │ ├── ViewModelUtil.kt │ │ │ ├── ViewModelDataBindingActivity.kt │ │ │ └── ViewModelDataBindingFragment.kt │ │ │ ├── network │ │ │ ├── NetworkResponse.kt │ │ │ ├── HeadInterceptor.kt │ │ │ ├── NetworkConfig.kt │ │ │ └── BaseNetworkClient.kt │ │ │ ├── exception │ │ │ ├── GenerateViewDataBindingException.kt │ │ │ └── NetWorkException.kt │ │ │ ├── viewmodel │ │ │ └── BaseViewModel.kt │ │ │ └── ext │ │ │ └── BaseViewModelExt.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── yeqiu │ │ │ └── fastmvvm │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── yeqiu │ │ └── fastmvvm │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── todo.txt ├── README.md ├── settings.gradle ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── config.gradle └── gradlew /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /easyAndroid/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /fastMvvm/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /fastMvvm/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /easyAndroid/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sample/src/main/res/values-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 48dp 3 | -------------------------------------------------------------------------------- /sample/src/main/res/values-w1240dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 200dp 3 | -------------------------------------------------------------------------------- /sample/src/main/res/values-w600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 48dp 3 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeqiu/HailHydra/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /easyAndroid/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /fastMvvm/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | 2 | 1.封装网络请求,自动联动到 activity 的loading 3 | 2.处理多个请求loading可能失效的问题 4 | 详情页面 5 | 登录页面 6 | 列表页面 7 | 单activity 8 | 9 | 10 | 主页面 11 | 12 | api基本使用 13 | 14 | 15 | 测试同步 -------------------------------------------------------------------------------- /sample/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/view/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.view 2 | 3 | /** 4 | * @project:FastMvvm 5 | * @author:小卷子 6 | * @date 2023/7/8 7 | * @describe: 8 | * @fix: 9 | */ 10 | open class BaseFragment { 11 | } -------------------------------------------------------------------------------- /sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/repository/MainRepository.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.repository 2 | 3 | /** 4 | * @project:FastMvvm 5 | * @author:小卷子 6 | * @date 2023/7/8 7 | * @describe: 8 | * @fix: 9 | */ 10 | class MainRepository { 11 | 12 | 13 | 14 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 05 21:41:36 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/login/LoginResult.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.login 2 | 3 | /** 4 | * Authentication result : success (user details) or error message. 5 | */ 6 | data class LoginResult( 7 | val success: LoggedInUserView? = null, 8 | val error: Int? = null 9 | ) -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/data/model/LoggedInUser.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.data.model 2 | 3 | /** 4 | * Data class that captures user information for logged in users retrieved from LoginRepository 5 | */ 6 | data class LoggedInUser( 7 | val userId: String, 8 | val displayName: String 9 | ) -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/login/LoggedInUserView.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.login 2 | 3 | /** 4 | * User details post authentication that is exposed to the UI 5 | */ 6 | data class LoggedInUserView( 7 | val displayName: String 8 | //... other data fields that may be accessible to the UI 9 | ) -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/login/LoginFormState.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.login 2 | 3 | /** 4 | * Data validation state of the login form. 5 | */ 6 | data class LoginFormState( 7 | val usernameError: Int? = null, 8 | val passwordError: Int? = null, 9 | val isDataValid: Boolean = false 10 | ) -------------------------------------------------------------------------------- /easyAndroid/src/main/java/com/yeqiu/easyandroid/Common.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.easyandroid 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | 6 | /** 7 | * @project:FastMvvm 8 | * @author:小卷子 9 | * @date 2023/7/9 10 | * @describe: 11 | * @fix: 12 | */ 13 | 14 | fun Context.showToast(msg:String){ 15 | Toast.makeText(this,msg,Toast.LENGTH_SHORT).show() 16 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## FastMvvm 2 | 3 | > master分支已修改为FastMvvm,原来HailHydra见分支[HailHydra](https://github.com/yeqiu/HailHydra/tree/HailHydra) 4 | 5 | 6 | 7 | 8 | 9 | **使用lifecycle+ViewModel+LiveData+DataBinding,快速实现MVVM架构** 10 | 11 | **使用kotlin扩展函数制作大量工具类,简化代码** 12 | 13 | **使用Retrofit+Moshi+协程,实现快速网络请求** 14 | 15 | **网络请求自动触发页面的loading,可配置统一错误处理** 16 | 17 | **可配置网络请求的脱壳数据** 18 | 19 | **详细使用见sample** 20 | 21 | -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/network/NetworkResponse.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.network 2 | 3 | /** 4 | * @project:FastMvvm 5 | * @author:小卷子 6 | * @date 2023/7/11 7 | * @describe: 8 | * @fix: 9 | */ 10 | abstract class NetworkResponse { 11 | 12 | abstract fun isSuccess():Boolean 13 | abstract fun getCode():Int 14 | abstract fun getResponse():T? 15 | abstract fun getErrorMessage():String 16 | 17 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /fastMvvm/src/test/java/com/yeqiu/fastmvvm/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /sample/src/test/java/com/fastmvvm/sample/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/home/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.home 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | 7 | class HomeViewModel : ViewModel() { 8 | 9 | private val _text = MutableLiveData().apply { 10 | value = "This is home Fragment" 11 | } 12 | val text: LiveData = _text 13 | } -------------------------------------------------------------------------------- /easyAndroid/src/test/java/com/yeqiu/easyandroid/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.easyandroid 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/dashboard/DashboardViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.dashboard 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | 7 | class DashboardViewModel : ViewModel() { 8 | 9 | private val _text = MutableLiveData().apply { 10 | value = "This is dashboard Fragment" 11 | } 12 | val text: LiveData = _text 13 | } -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/exception/GenerateViewDataBindingException.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.exception 2 | 3 | 4 | /** 5 | * @project:FastMvvm 6 | * @author:小卷子 7 | * @date 2023/7/8 8 | * @describe: 9 | * @fix: 10 | */ 11 | class GenerateViewDataBindingException : RuntimeException { 12 | constructor(message: String?) : super(message) 13 | constructor(message: String?, cause: Throwable?) : super(message, cause) 14 | constructor(cause: Throwable?) : super(cause) 15 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/notifications/NotificationsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.notifications 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | 7 | class NotificationsViewModel : ViewModel() { 8 | 9 | private val _text = MutableLiveData().apply { 10 | value = "This is notifications Fragment" 11 | } 12 | val text: LiveData = _text 13 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | maven { url 'https://jitpack.io' } 7 | jcenter() 8 | } 9 | } 10 | dependencyResolutionManagement { 11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 12 | repositories { 13 | google() 14 | mavenCentral() 15 | maven { url 'https://jitpack.io' } 16 | jcenter() 17 | } 18 | } 19 | rootProject.name = "FastMvvm" 20 | include ':fastMvvm' 21 | include ':sample' 22 | include ':easyAndroid' 23 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/data/Result.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.data 2 | 3 | /** 4 | * A generic class that holds a value with its loading status. 5 | * @param 6 | */ 7 | sealed class Result { 8 | 9 | data class Success(val data: T) : Result() 10 | data class Error(val exception: Exception) : Result() 11 | 12 | override fun toString(): String { 13 | return when (this) { 14 | is Success<*> -> "Success[data=$data]" 15 | is Error -> "Error[exception=$exception]" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/network/HeadInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.network 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | 6 | /** 7 | * @project:FastMvvm 8 | * @author:小卷子 9 | * @date 2023/7/10 10 | * @describe: 11 | * @fix: 12 | */ 13 | class HeadInterceptor: Interceptor { 14 | override fun intercept(chain: Interceptor.Chain): Response { 15 | val builder = chain.request().newBuilder() 16 | 17 | NetworkConfig.commonHead.forEach{ 18 | builder.addHeader(it.key,it.value) 19 | } 20 | return chain.proceed(builder.build()) 21 | } 22 | } -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/viewmodel/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | 6 | /** 7 | * @project:FastMvvm 8 | * @author:小卷子 9 | * @date 2023/7/9 10 | * @describe: 11 | * @fix: 12 | */ 13 | open class BaseViewModel : ViewModel() { 14 | 15 | val loadingStatus: OnLoadingEvent by lazy { OnLoadingEvent() } 16 | 17 | 18 | inner class OnLoadingEvent { 19 | //显示加载框 20 | val show by lazy { MutableLiveData() } 21 | 22 | //隐藏 23 | val dismiss by lazy { MutableLiveData() } 24 | } 25 | } -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/network/NetworkConfig.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.network 2 | 3 | import com.yeqiu.fastmvvm.BuildConfig 4 | import com.yeqiu.fastmvvm.exception.NetworkException 5 | 6 | 7 | /** 8 | * @project:FastMvvm 9 | * @author:小卷子 10 | * @date 2023/7/8 11 | * @describe: 12 | * @fix: 13 | */ 14 | object NetworkConfig { 15 | 16 | var enableLog = BuildConfig.DEBUG 17 | val commonHead = HashMap() 18 | var onNetworkError: (NetworkException) -> Unit = {} 19 | 20 | fun addCommonHead(vararg head: Pair) { 21 | head.forEach { 22 | commonHead[it.first] = it.second 23 | } 24 | 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /sample/src/main/res/menu/bottom_nav_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/network/ApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.network 2 | 3 | import com.yeqiu.fastmvvm.network.NetworkResponse 4 | 5 | /** 6 | * @project:FastMvvm 7 | * @author:小卷子 8 | * @date 2023/7/11 9 | * @describe: 10 | * @fix: 11 | */ 12 | data class ApiResponse(val errorCode:Int, val errorMsg:String, val data:T?):NetworkResponse(){ 13 | 14 | override fun isSuccess(): Boolean { 15 | return errorCode == 0 16 | } 17 | 18 | override fun getCode(): Int { 19 | return errorCode 20 | } 21 | 22 | override fun getResponse(): T? { 23 | return data 24 | } 25 | 26 | override fun getErrorMessage(): String { 27 | return errorMsg 28 | } 29 | 30 | 31 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/ 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | # External native build folder generated in Android Studio 2.2 and later 43 | .externalNativeBuild 44 | //build.gradle -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/fastmvvm/sample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.fastmvvm.sample", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /fastMvvm/src/androidTest/java/com/yeqiu/fastmvvm/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yeqiu.fasemvvm.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /easyAndroid/src/androidTest/java/com/yeqiu/easyandroid/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.easyandroid 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.yeqiu.easyandroid.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /fastMvvm/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /easyAndroid/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/exception/NetWorkException.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.exception 2 | 3 | 4 | /** 5 | * @project:FastMvvm 6 | * @author:小卷子 7 | * @date 2023/7/8 8 | * @describe: 9 | * @fix: 10 | */ 11 | 12 | const val jsonParseErrorCode = -1000 13 | const val checkResponseErrorCode = -1001 14 | const val NetworkErrorCode = -1002 15 | const val unknownErrorCode = -2000 16 | 17 | 18 | class NetworkException : RuntimeException { 19 | 20 | var errorMessage: String = "" 21 | var errorCode: Int = 0 22 | var throwable: Throwable? = null 23 | 24 | 25 | @JvmOverloads 26 | constructor(errorCode: Int = 0, errorMessage: String = "", throwable: Throwable? = null) { 27 | this.errorCode = errorCode 28 | this.errorMessage = errorMessage 29 | this.throwable = throwable 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/data/LoginDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.data 2 | 3 | import com.fastmvvm.sample.view.activity.data.model.LoggedInUser 4 | import java.io.IOException 5 | 6 | /** 7 | * Class that handles authentication w/ login credentials and retrieves user information. 8 | */ 9 | class LoginDataSource { 10 | 11 | fun login(username: String, password: String): Result { 12 | try { 13 | // TODO: handle loggedInUser authentication 14 | val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe") 15 | return Result.Success(fakeUser) 16 | } catch (e: Throwable) { 17 | return Result.Error(IOException("Error logging in", e)) 18 | } 19 | } 20 | 21 | fun logout() { 22 | // TODO: revoke authentication 23 | } 24 | } -------------------------------------------------------------------------------- /sample/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/network/Api.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.network 2 | 3 | import retrofit2.http.Field 4 | import retrofit2.http.FormUrlEncoded 5 | import retrofit2.http.GET 6 | import retrofit2.http.POST 7 | import retrofit2.http.Url 8 | 9 | /** 10 | * @project:FastMvvm 11 | * @author:小卷子 12 | * @date 2023/7/8 13 | * @describe: 14 | * @fix: 15 | */ 16 | interface Api { 17 | 18 | 19 | @GET 20 | suspend fun getUser( 21 | @Url url: String 22 | ): User 23 | 24 | 25 | @GET("/banner/json") 26 | suspend fun banner(): Banner 27 | 28 | @GET("/banner/json") 29 | suspend fun banner2(): ApiResponse> 30 | 31 | @FormUrlEncoded 32 | @POST("/user/login") 33 | suspend fun login( 34 | @Field("username") username: String, 35 | @Field("password") pwd: String 36 | ): ApiResponse 37 | 38 | } -------------------------------------------------------------------------------- /sample/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/network/Banner.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.network 2 | 3 | 4 | import com.squareup.moshi.Json 5 | 6 | data class Banner( 7 | @Json(name = "data") 8 | val `data`: List?, 9 | @Json(name = "errorCode") 10 | val errorCode: Int?, 11 | @Json(name = "errorMsg") 12 | val errorMsg: String? 13 | ) { 14 | data class Data( 15 | @Json(name = "desc") 16 | val desc: String?, 17 | @Json(name = "id") 18 | val id: Int?, 19 | @Json(name = "imagePath") 20 | val imagePath: String?, 21 | @Json(name = "isVisible") 22 | val isVisible: Int?, 23 | @Json(name = "order") 24 | val order: Int?, 25 | @Json(name = "title") 26 | val title: String?, 27 | @Json(name = "type") 28 | val type: Int?, 29 | @Json(name = "url") 30 | val url: String? 31 | ) 32 | } -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | fastMvvm 3 | BottomNavigationAcitvity 4 | Home 5 | Dashboard 6 | Notifications 7 | LoginActivity 8 | Email 9 | Password 10 | Sign in or register 11 | Sign in 12 | "Welcome !" 13 | Not a valid username 14 | Password must be >5 characters 15 | "Login failed" 16 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/network/NetworkClient.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.network 2 | 3 | import com.yeqiu.easyandroid.log 4 | import com.yeqiu.fastmvvm.network.BaseNetworkClient 5 | import okhttp3.OkHttpClient 6 | import retrofit2.Retrofit 7 | 8 | /** 9 | * @project:FastMvvm 10 | * @author:小卷子 11 | * @date 2023/7/10 12 | * @describe: 13 | * @fix: 14 | */ 15 | object NetworkClient : BaseNetworkClient() { 16 | 17 | override fun getBaseUrl(): String { 18 | // return "https://api.github.com" 19 | return "https://www.wanandroid.com" 20 | } 21 | 22 | override fun setOkHttpClient(builder: OkHttpClient.Builder) { 23 | } 24 | 25 | override fun setRetrofit(build: Retrofit.Builder) { 26 | } 27 | 28 | //双重校验锁式-单例 29 | val api: Api by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { 30 | "api by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED)".log() 31 | getService(Api::class.java) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /fastMvvm/src/main/java/com/yeqiu/fastmvvm/view/ViewModelUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.fastmvvm.view 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import androidx.fragment.app.Fragment 5 | import com.yeqiu.fastmvvm.viewmodel.BaseViewModel 6 | import java.lang.reflect.ParameterizedType 7 | 8 | 9 | /** 10 | * 获取当前类绑定的泛型ViewModel-clazz 11 | */ 12 | @Suppress("UNCHECKED_CAST") 13 | fun getViewModelClazz(obj: Any): VM { 14 | return (obj.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as VM 15 | } 16 | 17 | 18 | 19 | /** 20 | * 在Activity中得到Application上下文的ViewModel 21 | */ 22 | inline fun AppCompatActivity.getAppViewModel(): VM { 23 | TODO() 24 | } 25 | 26 | /** 27 | * 在Fragment中得到Application上下文的ViewModel 28 | * 提示,在fragment中调用该方法时,请在该Fragment onCreate以后调用或者请用by lazy方式懒加载初始化调用,不然会提示requireActivity没有导致错误 29 | */ 30 | inline fun Fragment.getAppViewModel(): VM { 31 | TODO() 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/login/LoginViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.login 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import com.fastmvvm.sample.view.activity.data.LoginDataSource 6 | import com.fastmvvm.sample.view.activity.data.LoginRepository 7 | 8 | /** 9 | * ViewModel provider factory to instantiate LoginViewModel. 10 | * Required given LoginViewModel has a non-empty constructor 11 | */ 12 | class LoginViewModelFactory : ViewModelProvider.Factory { 13 | 14 | @Suppress("UNCHECKED_CAST") 15 | override fun create(modelClass: Class): T { 16 | if (modelClass.isAssignableFrom(LoginViewModel::class.java)) { 17 | return LoginViewModel( 18 | loginRepository = LoginRepository( 19 | dataSource = LoginDataSource() 20 | ) 21 | ) as T 22 | } 23 | throw IllegalArgumentException("Unknown ViewModel class") 24 | } 25 | } -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_dashboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_notifications.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /sample/src/main/res/navigation/mobile_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/App.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModelProvider 6 | import androidx.lifecycle.ViewModelStore 7 | import androidx.lifecycle.ViewModelStoreOwner 8 | import com.yeqiu.easyandroid.log 9 | import com.yeqiu.fastmvvm.viewmodel.BaseViewModel 10 | 11 | 12 | /** 13 | * @project:FastMvvm 14 | * @author:小卷子 15 | * @date 2023/7/10 16 | * @describe: 17 | * @fix: 18 | */ 19 | class App : Application(), ViewModelStoreOwner { 20 | 21 | companion object { 22 | lateinit var instance: App 23 | lateinit var appViewModel: AppViewModel 24 | } 25 | 26 | override val viewModelStore: ViewModelStore 27 | get() = ViewModelStore() 28 | 29 | 30 | 31 | override fun onCreate() { 32 | super.onCreate() 33 | instance = this 34 | createAppViewModel() 35 | } 36 | 37 | private fun createAppViewModel() { 38 | "createAppViewModel" .log() 39 | appViewModel = ViewModelProvider(this).get(AppViewModel::class.java) 40 | } 41 | 42 | 43 | 44 | } 45 | 46 | class AppViewModel : BaseViewModel() { 47 | 48 | val data =MutableLiveData("data") 49 | 50 | } -------------------------------------------------------------------------------- /easyAndroid/src/main/java/com/yeqiu/easyandroid/ActivityStarter.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.easyandroid 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.os.Parcelable 7 | import java.io.Serializable 8 | 9 | /** 10 | * @project:FastMvvm 11 | * @author:小卷子 12 | * @date 2023/7/12 13 | * @describe: 14 | * @fix: 15 | */ 16 | inline fun Context.toActivity(vararg params: Pair) { 17 | 18 | val intent = Intent(this, T::class.java) 19 | putParams(intent, *params) 20 | if (this !is Activity){ 21 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 22 | } 23 | startActivity(intent) 24 | } 25 | 26 | fun putParams(intent: Intent, vararg params: Pair) { 27 | 28 | 29 | params.forEach { 30 | when (val value = it.second) { 31 | is Int -> {intent.putExtra(it.first,value)} 32 | is Long -> {intent.putExtra(it.first,value)} 33 | is Double ->{intent.putExtra(it.first,value)} 34 | is Boolean ->{intent.putExtra(it.first,value)} 35 | is String -> {intent.putExtra(it.first,value)} 36 | is Serializable -> {intent.putExtra(it.first,value)} 37 | is Parcelable -> {intent.putExtra(it.first,value)} 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/home/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.home 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.fragment.app.Fragment 9 | import androidx.lifecycle.ViewModelProvider 10 | import com.fastmvvm.sample.databinding.FragmentHomeBinding 11 | 12 | class HomeFragment : Fragment() { 13 | 14 | private var _binding: FragmentHomeBinding? = null 15 | 16 | // This property is only valid between onCreateView and 17 | // onDestroyView. 18 | private val binding get() = _binding!! 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View { 25 | val homeViewModel = 26 | ViewModelProvider(this).get(HomeViewModel::class.java) 27 | 28 | _binding = FragmentHomeBinding.inflate(inflater, container, false) 29 | val root: View = binding.root 30 | 31 | val textView: TextView = binding.textHome 32 | homeViewModel.text.observe(viewLifecycleOwner) { 33 | textView.text = it 34 | } 35 | return root 36 | } 37 | 38 | override fun onDestroyView() { 39 | super.onDestroyView() 40 | _binding = null 41 | } 42 | } -------------------------------------------------------------------------------- /sample/src/main/res/layout/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 24 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /easyAndroid/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | def config = rootProject.ext 8 | 9 | android { 10 | namespace config.namespace.easyAndroid 11 | compileSdk config.android.compileSdk 12 | 13 | defaultConfig { 14 | minSdk config.android.minSdk 15 | targetSdk config.android.targetSdk 16 | versionCode config.android.versionCode 17 | versionName config.android.versionName 18 | 19 | testInstrumentationRunner config.android.testInstrumentationRunner 20 | } 21 | 22 | buildTypes { 23 | 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | compileOptions { 31 | sourceCompatibility config.javaOptions.sourceCompatibility 32 | targetCompatibility config.javaOptions.targetCompatibility 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = config.javaOptions.kotlinOptions 37 | } 38 | 39 | buildFeatures { 40 | dataBinding = true 41 | } 42 | 43 | } 44 | 45 | dependencies { 46 | 47 | implementation config.androidLibrary.androidxAppcompat 48 | implementation config.androidLibrary.androidxKtx 49 | implementation config.kotlinLibrary.coroutinesCore 50 | implementation config.kotlinLibrary.coroutinesAndroid 51 | 52 | 53 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/dashboard/DashboardFragment.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.dashboard 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.fragment.app.Fragment 9 | import androidx.lifecycle.ViewModelProvider 10 | import com.fastmvvm.sample.databinding.FragmentDashboardBinding 11 | 12 | class DashboardFragment : Fragment() { 13 | 14 | private var _binding: FragmentDashboardBinding? = null 15 | 16 | // This property is only valid between onCreateView and 17 | // onDestroyView. 18 | private val binding get() = _binding!! 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View { 25 | val dashboardViewModel = 26 | ViewModelProvider(this).get(DashboardViewModel::class.java) 27 | 28 | _binding = FragmentDashboardBinding.inflate(inflater, container, false) 29 | val root: View = binding.root 30 | 31 | val textView: TextView = binding.textDashboard 32 | dashboardViewModel.text.observe(viewLifecycleOwner) { 33 | textView.text = it 34 | } 35 | return root 36 | } 37 | 38 | override fun onDestroyView() { 39 | super.onDestroyView() 40 | _binding = null 41 | } 42 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | 25 | android.injected.testOnly=false 26 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/notifications/NotificationsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.notifications 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.fragment.app.Fragment 9 | import androidx.lifecycle.ViewModelProvider 10 | import com.fastmvvm.sample.databinding.FragmentNotificationsBinding 11 | 12 | class NotificationsFragment : Fragment() { 13 | 14 | private var _binding: FragmentNotificationsBinding? = null 15 | 16 | // This property is only valid between onCreateView and 17 | // onDestroyView. 18 | private val binding get() = _binding!! 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View { 25 | val notificationsViewModel = 26 | ViewModelProvider(this).get(NotificationsViewModel::class.java) 27 | 28 | _binding = FragmentNotificationsBinding.inflate(inflater, container, false) 29 | val root: View = binding.root 30 | 31 | val textView: TextView = binding.textNotifications 32 | notificationsViewModel.text.observe(viewLifecycleOwner) { 33 | textView.text = it 34 | } 35 | return root 36 | } 37 | 38 | override fun onDestroyView() { 39 | super.onDestroyView() 40 | _binding = null 41 | } 42 | } -------------------------------------------------------------------------------- /easyAndroid/src/main/java/com/yeqiu/easyandroid/AppInfo.kt: -------------------------------------------------------------------------------- 1 | package com.yeqiu.easyandroid 2 | 3 | import android.content.Context 4 | 5 | /** 6 | * @project:FastMvvm 7 | * @author:小卷子 8 | * @date 2023/7/12 9 | * @describe: 10 | * @fix: 11 | */ 12 | 13 | fun Context.getAppName(): String { 14 | 15 | return "unKnown" 16 | } 17 | 18 | fun Context.getChannelName(): String { 19 | 20 | return "unKnown" 21 | } 22 | 23 | fun Context.getVersionName(): String { 24 | 25 | return "unKnown" 26 | } 27 | 28 | fun Context.getVersionCode(): String { 29 | 30 | return "unKnown" 31 | } 32 | 33 | fun Context.getAppIcon(): String { 34 | 35 | return "unKnown" 36 | } 37 | 38 | fun Context.getPackageName(): String { 39 | 40 | return "unKnown" 41 | } 42 | 43 | fun Context.getFirstInstallTime(): String { 44 | 45 | return "unKnown" 46 | } 47 | 48 | fun Context.getAppSize(): String { 49 | 50 | return "unKnown" 51 | } 52 | 53 | fun Context.getAppApk(): String { 54 | 55 | return "unKnown" 56 | } 57 | 58 | fun Context.isInstalled(): String { 59 | 60 | return "unKnown" 61 | } 62 | 63 | fun Context.getPackageNameLast(): String { 64 | 65 | return "unKnown" 66 | } 67 | 68 | fun Context.killProcesses(): String { 69 | 70 | return "unKnown" 71 | } 72 | 73 | fun Context.getSystemVersion(): String { 74 | 75 | return "unKnown" 76 | } 77 | 78 | fun Context.isDebug(): String { 79 | 80 | return "unKnown" 81 | } 82 | 83 | 84 | /////////AppUtil 85 | fun Context.openOtherApp(): String { 86 | 87 | return "unKnown" 88 | } 89 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/data/LoginRepository.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.data 2 | 3 | import com.fastmvvm.sample.view.activity.data.model.LoggedInUser 4 | 5 | /** 6 | * Class that requests authentication and user information from the remote data source and 7 | * maintains an in-memory cache of login status and user credentials information. 8 | */ 9 | 10 | class LoginRepository(val dataSource: LoginDataSource) { 11 | 12 | // in-memory cache of the loggedInUser object 13 | var user: LoggedInUser? = null 14 | private set 15 | 16 | val isLoggedIn: Boolean 17 | get() = user != null 18 | 19 | init { 20 | // If user credentials will be cached in local storage, it is recommended it be encrypted 21 | // @see https://developer.android.com/training/articles/keystore 22 | user = null 23 | } 24 | 25 | fun logout() { 26 | user = null 27 | dataSource.logout() 28 | } 29 | 30 | fun login(username: String, password: String): Result { 31 | // handle login 32 | val result = dataSource.login(username, password) 33 | 34 | if (result is Result.Success) { 35 | setLoggedInUser(result.data) 36 | } 37 | 38 | return result 39 | } 40 | 41 | private fun setLoggedInUser(loggedInUser: LoggedInUser) { 42 | this.user = loggedInUser 43 | // If user credentials will be cached in local storage, it is recommended it be encrypted 44 | // @see https://developer.android.com/training/articles/keystore 45 | } 46 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_bottom_navigation_acitvity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 32 | 33 | -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity 2 | 3 | import android.widget.TextView 4 | import androidx.databinding.ViewDataBinding 5 | import com.afollestad.materialdialogs.MaterialDialog 6 | import com.afollestad.materialdialogs.customview.customView 7 | import com.afollestad.materialdialogs.customview.getCustomView 8 | import com.afollestad.materialdialogs.lifecycle.lifecycleOwner 9 | import com.fastmvvm.sample.R 10 | import com.yeqiu.fastmvvm.view.ViewModelDataBindingActivity 11 | import com.yeqiu.fastmvvm.viewmodel.BaseViewModel 12 | 13 | /** 14 | * @project:FastMvvm 15 | * @author:小卷子 16 | * @date 2023/7/9 17 | * @describe: 18 | * @fix: 19 | */ 20 | abstract class BaseActivity : ViewModelDataBindingActivity() { 21 | 22 | 23 | private lateinit var loadingDialog: MaterialDialog 24 | 25 | 26 | override fun showLoading(message:String) { 27 | 28 | 29 | if (!::loadingDialog.isInitialized) { 30 | loadingDialog = MaterialDialog(this) 31 | .cancelable(true) 32 | .cancelOnTouchOutside(false) 33 | .cornerRadius(12f) 34 | .customView(R.layout.dialog_loading) 35 | .lifecycleOwner(this) 36 | loadingDialog.getCustomView().run { 37 | (findViewById(R.id.loading_tips)).text =message 38 | } 39 | } 40 | 41 | loadingDialog.show() 42 | } 43 | 44 | override fun dismissLoading() { 45 | 46 | if (::loadingDialog.isInitialized){ 47 | loadingDialog.dismiss() 48 | } 49 | 50 | } 51 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/BottomNavigationAcitvity.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.navigation.findNavController 6 | import androidx.navigation.ui.AppBarConfiguration 7 | import androidx.navigation.ui.setupActionBarWithNavController 8 | import androidx.navigation.ui.setupWithNavController 9 | import com.fastmvvm.sample.R 10 | import com.fastmvvm.sample.databinding.ActivityBottomNavigationAcitvityBinding 11 | import com.google.android.material.bottomnavigation.BottomNavigationView 12 | 13 | class BottomNavigationAcitvity : AppCompatActivity() { 14 | 15 | private lateinit var binding: ActivityBottomNavigationAcitvityBinding 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | 20 | binding = ActivityBottomNavigationAcitvityBinding.inflate(layoutInflater) 21 | setContentView(binding.root) 22 | 23 | val navView: BottomNavigationView = binding.navView 24 | 25 | val navController = 26 | findNavController(R.id.nav_host_fragment_activity_bottom_navigation_acitvity) 27 | // Passing each menu ID as a set of Ids because each 28 | // menu should be considered as top level destinations. 29 | val appBarConfiguration = AppBarConfiguration( 30 | setOf( 31 | R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications 32 | ) 33 | ) 34 | setupActionBarWithNavController(navController, appBarConfiguration) 35 | navView.setupWithNavController(navController) 36 | } 37 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /fastMvvm/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | def config = rootProject.ext 8 | 9 | android { 10 | namespace config.namespace.fastMvvm 11 | compileSdk config.android.compileSdk 12 | 13 | defaultConfig { 14 | minSdk config.android.minSdk 15 | targetSdk config.android.targetSdk 16 | versionCode config.android.versionCode 17 | versionName config.android.versionName 18 | 19 | testInstrumentationRunner config.android.testInstrumentationRunner 20 | } 21 | 22 | buildTypes { 23 | 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | compileOptions { 31 | sourceCompatibility config.javaOptions.sourceCompatibility 32 | targetCompatibility config.javaOptions.targetCompatibility 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = config.javaOptions.kotlinOptions 37 | } 38 | 39 | dataBinding { 40 | enable = true 41 | } 42 | 43 | } 44 | 45 | dependencies { 46 | 47 | implementation config.androidLibrary.androidxAppcompat 48 | implementation config.androidLibrary.androidxKtx 49 | implementation config.kotlinLibrary.coroutinesCore 50 | implementation config.kotlinLibrary.coroutinesAndroid 51 | implementation project(path: ':easyAndroid') 52 | 53 | api config.androidLibrary.lifecycleRuntime 54 | api config.androidLibrary.lifecycleViewmodel 55 | 56 | api config.library.retrofit 57 | api config.library.moshiConverter 58 | kapt config.library.moshiKotlinCodegen 59 | implementation config.library.okhttpLogginginterceptor 60 | implementation config.library.logginginterceptor 61 | implementation config.library.moshiKotlin 62 | 63 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/DetailActivity.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity 2 | 3 | import android.os.Bundle 4 | import android.widget.ImageView 5 | import androidx.databinding.BindingAdapter 6 | import androidx.lifecycle.MutableLiveData 7 | import com.bumptech.glide.Glide 8 | import com.fastmvvm.sample.databinding.ActivityDetailBinding 9 | import com.fastmvvm.sample.network.NetworkClient 10 | import com.fastmvvm.sample.network.User 11 | import com.yeqiu.easyandroid.showToast 12 | import com.yeqiu.fastmvvm.ext.requestOriginalData 13 | import com.yeqiu.fastmvvm.viewmodel.BaseViewModel 14 | import kotlinx.coroutines.delay 15 | 16 | 17 | class DetailActivity : BaseActivity() { 18 | 19 | 20 | override fun initData(savedInstanceState: Bundle?) { 21 | binding.mainViewModel = viewModel 22 | 23 | viewModel.getData() 24 | 25 | } 26 | 27 | override fun addObserve() { 28 | 29 | viewModel.errorMsg.observe(this) { 30 | showToast(it) 31 | } 32 | 33 | } 34 | 35 | 36 | } 37 | 38 | 39 | class ImageLoader { 40 | companion object { 41 | @JvmStatic 42 | @BindingAdapter("url") 43 | fun loadUrl(imageView: ImageView, url: String?) { 44 | url?.let { 45 | Glide.with(imageView.context) 46 | .load(it) 47 | .into(imageView) 48 | } 49 | 50 | 51 | } 52 | } 53 | } 54 | 55 | 56 | class DetailViewModel : BaseViewModel() { 57 | 58 | val user = MutableLiveData() 59 | val errorMsg = MutableLiveData() 60 | 61 | fun getData() { 62 | 63 | requestOriginalData( 64 | { 65 | delay(2000) 66 | NetworkClient.api.getUser("https://api.github.com/users/yeqiu") 67 | }, 68 | { user.value = it } 69 | ) 70 | 71 | 72 | } 73 | 74 | fun testError() { 75 | 76 | 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/network/LoginInfo.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.network 2 | 3 | 4 | import com.squareup.moshi.Json 5 | 6 | data class LoginInfo( 7 | @Json(name = "admin") 8 | val admin: Boolean?, 9 | @Json(name = "chapterTops") 10 | val chapterTops: List?, 11 | @Json(name = "coinCount") 12 | val coinCount: Int?, 13 | @Json(name = "collectIds") 14 | val collectIds: List?, 15 | @Json(name = "email") 16 | val email: String?, 17 | @Json(name = "icon") 18 | val icon: String?, 19 | @Json(name = "id") 20 | val id: Int?, 21 | @Json(name = "nickname") 22 | val nickname: String?, 23 | @Json(name = "password") 24 | val password: String?, 25 | @Json(name = "publicName") 26 | val publicName: String?, 27 | @Json(name = "token") 28 | val token: String?, 29 | @Json(name = "type") 30 | val type: Int?, 31 | @Json(name = "username") 32 | val username: String? 33 | ) { 34 | } 35 | 36 | //data class LoginInfo( 37 | // @Json(name = "data") 38 | // val `data`: Data?, 39 | // @Json(name = "errorCode") 40 | // val errorCode: Int?, 41 | // @Json(name = "errorMsg") 42 | // val errorMsg: String? 43 | //) { 44 | // data class Data( 45 | // @Json(name = "admin") 46 | // val admin: Boolean?, 47 | // @Json(name = "chapterTops") 48 | // val chapterTops: List?, 49 | // @Json(name = "coinCount") 50 | // val coinCount: Int?, 51 | // @Json(name = "collectIds") 52 | // val collectIds: List?, 53 | // @Json(name = "email") 54 | // val email: String?, 55 | // @Json(name = "icon") 56 | // val icon: String?, 57 | // @Json(name = "id") 58 | // val id: Int?, 59 | // @Json(name = "nickname") 60 | // val nickname: String?, 61 | // @Json(name = "password") 62 | // val password: String?, 63 | // @Json(name = "publicName") 64 | // val publicName: String?, 65 | // @Json(name = "token") 66 | // val token: String?, 67 | // @Json(name = "type") 68 | // val type: Int?, 69 | // @Json(name = "username") 70 | // val username: String? 71 | // ) 72 | //} -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.viewModelScope 6 | import com.fastmvvm.sample.databinding.ActivityMainBinding 7 | import com.yeqiu.easyandroid.toActivity 8 | import com.yeqiu.fastmvvm.viewmodel.BaseViewModel 9 | import kotlinx.coroutines.delay 10 | import kotlinx.coroutines.launch 11 | 12 | class MainActivity : BaseActivity() { 13 | override fun initData(savedInstanceState: Bundle?) { 14 | binding.viewModle = viewModel 15 | } 16 | 17 | override fun addObserve() { 18 | 19 | viewModel.detail.observe(this) { 20 | 21 | if (it) { 22 | toActivity() 23 | } 24 | 25 | } 26 | viewModel.login.observe(this) { 27 | if (it) { 28 | toActivity() 29 | } 30 | } 31 | viewModel.singleActivity.observe(this) { 32 | if (it) { 33 | toActivity() 34 | } 35 | } 36 | viewModel.netWork.observe(this) { 37 | 38 | if (it) { 39 | toActivity() 40 | } 41 | 42 | } 43 | 44 | 45 | } 46 | 47 | } 48 | 49 | class MainViewModel : BaseViewModel() { 50 | 51 | val detail = MutableLiveData(false) 52 | val login = MutableLiveData(false) 53 | val singleActivity = MutableLiveData(false) 54 | val netWork = MutableLiveData(false) 55 | 56 | 57 | fun clickDetail() { 58 | detail.value = true 59 | } 60 | 61 | fun clickLogin() { 62 | login.value = true 63 | } 64 | 65 | fun clickSingleActivity() { 66 | singleActivity.value = true 67 | } 68 | 69 | fun clickNetwork() { 70 | netWork.value = true 71 | } 72 | fun clickLoading() { 73 | 74 | //三秒后自动关闭 75 | viewModelScope.launch { 76 | loadingStatus.show.postValue("加载。。。") 77 | delay(3000) 78 | loadingStatus.dismiss.postValue(true) 79 | } 80 | } 81 | 82 | 83 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/fastmvvm/sample/view/activity/ui/login/LoginViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.fastmvvm.sample.view.activity.ui.login 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import android.util.Patterns 7 | import com.fastmvvm.sample.R 8 | import com.fastmvvm.sample.view.activity.data.LoginRepository 9 | import com.fastmvvm.sample.view.activity.data.Result 10 | 11 | 12 | class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() { 13 | 14 | private val _loginForm = MutableLiveData() 15 | val loginFormState: LiveData = _loginForm 16 | 17 | private val _loginResult = MutableLiveData() 18 | val loginResult: LiveData = _loginResult 19 | 20 | fun login(username: String, password: String) { 21 | // can be launched in a separate asynchronous job 22 | val result = loginRepository.login(username, password) 23 | 24 | if (result is Result.Success) { 25 | _loginResult.value = 26 | LoginResult(success = LoggedInUserView(displayName = result.data.displayName)) 27 | } else { 28 | _loginResult.value = LoginResult(error = R.string.login_failed) 29 | } 30 | } 31 | 32 | fun loginDataChanged(username: String, password: String) { 33 | if (!isUserNameValid(username)) { 34 | _loginForm.value = LoginFormState(usernameError = R.string.invalid_username) 35 | } else if (!isPasswordValid(password)) { 36 | _loginForm.value = LoginFormState(passwordError = R.string.invalid_password) 37 | } else { 38 | _loginForm.value = LoginFormState(isDataValid = true) 39 | } 40 | } 41 | 42 | // A placeholder username validation check 43 | private fun isUserNameValid(username: String): Boolean { 44 | return if (username.contains('@')) { 45 | Patterns.EMAIL_ADDRESS.matcher(username).matches() 46 | } else { 47 | username.isNotBlank() 48 | } 49 | } 50 | 51 | // A placeholder password validation check 52 | private fun isPasswordValid(password: String): Boolean { 53 | return password.length > 5 54 | } 55 | } -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 20 | 21 | 22 |