├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── config.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ljb │ │ └── mvp │ │ └── kotlin │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ljb │ │ │ └── mvp │ │ │ └── kotlin │ │ │ ├── MVPKotlinApplication.kt │ │ │ ├── adapter │ │ │ ├── MainTabAdapter.kt │ │ │ ├── MyTabAdapter.kt │ │ │ └── rv │ │ │ │ ├── EventAdapter.kt │ │ │ │ ├── FollowersAdapter.kt │ │ │ │ ├── FollowersDecoration.kt │ │ │ │ ├── FollowingAdapter.kt │ │ │ │ ├── RepositoriesAdapter.kt │ │ │ │ └── StarredAdapter.kt │ │ │ ├── common │ │ │ ├── Constant.kt │ │ │ ├── LoginUser.kt │ │ │ ├── ex │ │ │ │ └── BaseActivityLifecycleCallbacks.kt │ │ │ └── rx │ │ │ │ ├── BaseNetObserver.kt │ │ │ │ ├── BaseObserver.kt │ │ │ │ ├── RxEx.kt │ │ │ │ └── RxUtils.kt │ │ │ ├── contract │ │ │ ├── EventsContract.kt │ │ │ ├── FollowersContract.kt │ │ │ ├── FollowingContract.kt │ │ │ ├── HomeContract.kt │ │ │ ├── LoginContract.kt │ │ │ ├── MyContract.kt │ │ │ ├── RepositoriesContract.kt │ │ │ ├── StarredContract.kt │ │ │ ├── WebViewContract.kt │ │ │ └── base │ │ │ │ └── ListContract.kt │ │ │ ├── db │ │ │ ├── DatabaseHelperImpl.kt │ │ │ └── DatabaseSqlHelper.kt │ │ │ ├── domain │ │ │ ├── Event.kt │ │ │ ├── Follower.kt │ │ │ ├── Following.kt │ │ │ ├── GitHubHttpError.kt │ │ │ ├── MyTabFragmentBean.kt │ │ │ ├── Repository.kt │ │ │ ├── Starred.kt │ │ │ ├── TabBean.kt │ │ │ └── User.kt │ │ │ ├── img │ │ │ ├── ImageLoader.kt │ │ │ └── OkHttpAppGlideModule.kt │ │ │ ├── model │ │ │ ├── EventsModel.kt │ │ │ ├── FollowersModel.kt │ │ │ ├── FollowingModel.kt │ │ │ ├── HomeModel.kt │ │ │ ├── LoginModel.kt │ │ │ ├── MyModel.kt │ │ │ ├── RepositoriesModel.kt │ │ │ ├── StarredModel.kt │ │ │ └── WebViewModel.kt │ │ │ ├── presenter │ │ │ ├── EventsPresenter.kt │ │ │ ├── FollowersPresenter.kt │ │ │ ├── FollowingPresenter.kt │ │ │ ├── HomePresenter.kt │ │ │ ├── LoginPresenter.kt │ │ │ ├── MyPresenter.kt │ │ │ ├── RepositoriesPresenter.kt │ │ │ ├── StarredPresenter.kt │ │ │ └── WebViewPresenter.kt │ │ │ ├── protocol │ │ │ ├── dao │ │ │ │ ├── IUserDaoProtocol.kt │ │ │ │ ├── ProtocolConfig.kt │ │ │ │ └── impl │ │ │ │ │ └── UserDaoProtocol.kt │ │ │ └── http │ │ │ │ ├── IReposHttpProtocol.kt │ │ │ │ └── IUserHttpProtocol.kt │ │ │ ├── table │ │ │ └── UserTable.kt │ │ │ ├── utils │ │ │ ├── JsonParser.kt │ │ │ ├── PermissionUtils.kt │ │ │ ├── SPUtils.kt │ │ │ ├── UIUtils.kt │ │ │ └── XLog.kt │ │ │ ├── view │ │ │ ├── act │ │ │ │ ├── HomeActivity.kt │ │ │ │ ├── LoginActivity.kt │ │ │ │ └── WebViewActivity.kt │ │ │ └── fragment │ │ │ │ ├── EventsFragment.kt │ │ │ │ ├── FollowersFragment.kt │ │ │ │ ├── FollowingFragment.kt │ │ │ │ ├── MyFragment.kt │ │ │ │ ├── RepositoriesFragment.kt │ │ │ │ └── StarredFragment.kt │ │ │ ├── webview │ │ │ ├── JsApi.kt │ │ │ ├── SimpleWebActionCallBack.kt │ │ │ ├── WebActionCallBack.kt │ │ │ └── WebViewProxy.kt │ │ │ └── widget │ │ │ ├── ProgressWheel.kt │ │ │ ├── TabGroupView.kt │ │ │ ├── dialog │ │ │ ├── LoadingDialog.kt │ │ │ └── NormalMsgDialog.kt │ │ │ └── loadmore │ │ │ ├── LoadMoreHolder.kt │ │ │ └── LoadMoreRecyclerAdapter.kt │ └── res │ │ ├── anim │ │ ├── share_dialog_enter.xml │ │ └── share_dialog_out.xml │ │ ├── drawable-hdpi │ │ └── icon_app.png │ │ ├── drawable-mdpi │ │ └── icon_app.png │ │ ├── drawable-xhdpi │ │ ├── icon_app.png │ │ ├── icon_back.png │ │ ├── icon_company.png │ │ ├── icon_location.png │ │ ├── icon_logo_github.png │ │ ├── icon_page_empty.png │ │ └── icon_page_error.png │ │ ├── drawable-xxhdpi │ │ ├── bg_my_header.png │ │ ├── bottom_tab_box_normal.png │ │ ├── bottom_tab_box_pressed.png │ │ ├── bottom_tab_following_normal.png │ │ ├── bottom_tab_following_pressed.png │ │ ├── bottom_tab_my_normal.png │ │ ├── bottom_tab_my_pressed.png │ │ ├── default_header.png │ │ ├── icon_app.png │ │ └── icon_user_github.png │ │ ├── drawable-xxxhdpi │ │ ├── icon_app.png │ │ └── test_header.jpg │ │ ├── drawable │ │ ├── bottom_tab_following.xml │ │ ├── bottom_tab_my.xml │ │ ├── bottom_tab_repos.xml │ │ ├── selector_tab_text.xml │ │ ├── shape_loading_dialog_bg.xml │ │ ├── shape_reload_bg.xml │ │ ├── shape_round_gray.xml │ │ └── shape_round_white_transparent.xml │ │ ├── layout │ │ ├── activity_home.xml │ │ ├── activity_login.xml │ │ ├── activity_web.xml │ │ ├── dialog_loading.xml │ │ ├── dialog_msg_normal.xml │ │ ├── fragment_events.xml │ │ ├── fragment_followers.xml │ │ ├── fragment_following.xml │ │ ├── fragment_my.xml │ │ ├── fragment_repos.xml │ │ ├── fragment_starred.xml │ │ ├── item_event.xml │ │ ├── item_followers.xml │ │ ├── item_following.xml │ │ ├── item_starred.xml │ │ ├── layout_bottom_tab_defalut.xml │ │ ├── layout_common_title.xml │ │ ├── layout_load_more.xml │ │ ├── layout_page_empty.xml │ │ ├── layout_page_error.xml │ │ ├── layout_page_load.xml │ │ ├── layout_recycler_view.xml │ │ ├── layout_refresh_recycler_view.xml │ │ └── layout_web.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── ljb │ └── mvp │ └── kotlin │ └── ExampleUnitTest.java ├── build.gradle ├── daolib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── dao │ │ └── ljb │ │ └── kt │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── dao │ │ │ └── ljb │ │ │ └── kt │ │ │ ├── DaoConfig.kt │ │ │ ├── core │ │ │ ├── BaseDaoProtocol.kt │ │ │ ├── DaoFactory.kt │ │ │ ├── DaoProtocolGroup.kt │ │ │ ├── DatabaseHelper.kt │ │ │ ├── IDaoInterface.kt │ │ │ └── IDaoProtocolConfig.kt │ │ │ ├── db │ │ │ └── DatabaseOpenHelper.kt │ │ │ └── table │ │ │ └── BaseTable.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── dao │ └── ljb │ └── kt │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── anim.gif ├── model.png ├── mvp.png ├── mvp_plugin.gif ├── plugin_install.png └── qrcode.png ├── netlib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── senyint │ │ └── netlib │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── net │ │ │ └── ljb │ │ │ └── kt │ │ │ ├── HttpConfig.kt │ │ │ ├── client │ │ │ ├── HttpClient.kt │ │ │ ├── HttpFactory.kt │ │ │ ├── HttpMethod.kt │ │ │ └── StringConverterFactory.java │ │ │ ├── interceptor │ │ │ └── AddGlobalParamInterceptor.kt │ │ │ └── utils │ │ │ └── NetLog.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── senyint │ └── netlib │ └── ExampleUnitTest.java ├── settings.gradle └── sh.exe.stackdump /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .apk 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MVPKotlin 2 | 3 | [https://github.com/cn-ljb/mvp-kotlin](https://github.com/cn-ljb/mvp-kotlin "MVPKotlin") 4 | 5 | > 快捷、高效、低耦合的Android MVP架构,支持Java、Kotlin混编。 6 | 7 | ![kotlin-mvp](./img/mvp_plugin.gif) 8 | 9 | ## 视频演示 10 | 11 | [[高清视频,点击这里]](https://cn-ljb.github.io/2019/07/08/Kotlin-MVP%E6%9E%B6%E6%9E%84%E8%A7%86%E9%A2%91%E6%BC%94%E7%A4%BA/ "视频演示") 12 | 13 | 14 | 扫码查看Demo App: 15 | 16 | ![mvp](./img/qrcode.png) 17 | 18 | 19 | * [目录](#) 20 | * [集成方式](#res1) 21 | * [概述](#res2) 22 | * [代码示例](#res3) 23 | * [Kotlin MVP Auto 插件](#res4) 24 | * [截图](#res5) 25 | 26 | ##
集成方式
27 | 28 | > * 1、Project的**build.gradle**文件添加如下代码: 29 | 30 | allprojects { 31 | repositories { 32 | ... 33 | maven { url 'https://jitpack.io' } 34 | } 35 | } 36 | 37 | > * 2、主Module的**build.gradle**添加依赖: 38 | 39 | //mvp core 40 | implementation 'com.github.cn-ljb:kotlin-mvp-lib:1.2.0' 41 | 42 | //网络库lib、数据库lib 根据项目实际情况引入 43 | //net lib 44 | implementation 'com.github.cn-ljb:netlib:1.0.1' 45 | //dao lib 46 | implementation 'com.github.cn-ljb:daolib:1.0.1' 47 | 48 | lib源码:[kotlin-mvp-lib](https://github.com/cn-ljb/kotlin-mvp-lib)、[net-lib](https://github.com/cn-ljb/netlib)、[dao-lib](https://github.com/cn-ljb/daolib) 49 | 50 | 51 | ##
概述
52 | 53 | > **为什么要使用MVP架构?** 54 | 55 | 通常Android项目结构中,我们会在Activity\Fragment中编写大量代码,例如:网络请求、IO操作、数据填充、页面切换等,这种项目结构宏观的称之为MVC。 56 | 57 | **MVC**:我们可以把数据源(网络请求、IO...)看作Model层,xml等布局文件看作View层,Activity\Fragment看作Controller层。但在android中xml能力太薄弱了,以至于Activity做了很多本不属于它的工作。 58 | 59 | **MVP**:在MVP架构中Model层与MVC一样存放数据源(网络请求、IO...),将Activity\Fragment都看作为View层,仅负责UI展示和数据填充,将Model层与View层的交互操作交给Presenter层。 60 | 61 | > **MVP架构图** 62 | 63 | ![mvp](./img/mvp.png) 64 | 65 | > **特点** 66 | 67 | * 1、V层由Activity和Fragmen组成,且仅负责UI展示、数据填充等工作,分工明确; 68 | * 2、M层完全与V层隔离,P层作为V层与M层的桥梁,承担中间人角色(V通过P获取M数据); 69 | * 3、V层与P层对象相互持有,通过Constract限制两者的访问域,降低耦合; 70 | * 4、P层持有M层对象,通过Constract限制P层可访问域,降低耦合; 71 | 72 | > **扩展** 73 | 74 | 考虑到实际项目中Model层主要操作是net和db,为了统一调用api,对net和db进行了封装,通过Factory.getProtocol()产出具体的操作实例。 75 | 76 | [net-lib](https://github.com/cn-ljb/netlib): rxjava2 + rxAndroid + okhttp3 + retrofit2 77 | 78 | [dao-lib](https://github.com/cn-ljb/daolib): rxjava2 + rxAndroid + sqlite 79 | 80 | ![mvp](./img/model.png) 81 | 82 | 83 | ##
代码示例
84 | 85 | [[祥见视频演示]](https://cn-ljb.github.io/2019/07/08/Kotlin-MVP%E6%9E%B6%E6%9E%84%E8%A7%86%E9%A2%91%E6%BC%94%E7%A4%BA/ "视频演示") 86 | 87 | ##
Kotlin MVP Auto 插件
88 | 89 | 我们知道View、Presenter、Model、Constact需要编写固定的套路代码来进行关联,比如集成某一个Base类,实现某个固定接口。 90 | 91 | 为了提高开发效率,配合该MVP库专门为开发者提供[Kotlin MVP Auto插件](https://github.com/cn-ljb/kotlin-mvp-plugin "Kotlin MVP Auto")来帮你统统搞定。 92 | 93 | ### 安装插件 94 | 95 | > 操作: File -> Settings -> Plugins -> Kotlin MVP Auto -> install 96 | 97 | ![plugin_install](./img/plugin_install.png) 98 | 99 | ### 插件演示 100 | 101 | * 自动生成View、Presenter、Model、Contract Kotlin文件 102 | 103 | > 操作:包目录右键 -> New MVP Kotlin -> 输入模块名称 -> OK 104 | 105 | ![kotlin-mvp](./img/mvp_plugin.gif) 106 | 107 | 108 | * 自动生成View、Presenter、Model、Contract Java文件 109 | 110 | > 操作:包目录右键 -> New MVP Java -> 输入模块名称 -> OK 111 | 112 | ##
Demo App 截图
113 | 114 | ![simple](./img/anim.gif) 115 | 116 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | apply from: 'config.gradle' 6 | 7 | android { 8 | compileSdkVersion 28 9 | buildToolsVersion '28.0.3' 10 | defaultConfig { 11 | applicationId "com.ljb.mvp.kotlin" 12 | minSdkVersion 16 13 | targetSdkVersion 28 14 | versionCode 100 15 | versionName "1.0.0" 16 | } 17 | buildTypes { 18 | release { 19 | buildConfigField("boolean", "VERSION_DEBUG", "$VERSION_DEBUG") 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | debug { 24 | buildConfigField("boolean", "VERSION_DEBUG", "$VERSION_DEBUG") 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(include: ['*.jar'], dir: 'libs') 31 | testImplementation 'junit:junit:4.12' 32 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 33 | 34 | //androidX 35 | implementation 'androidx.core:core:1.0.2' 36 | implementation 'androidx.appcompat:appcompat:1.0.2' 37 | implementation 'com.google.android.material:material:1.0.0' 38 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 39 | implementation 'androidx.viewpager:viewpager:1.0.0' 40 | implementation 'androidx.fragment:fragment:1.0.0' 41 | 42 | //bugly 43 | implementation 'com.tencent.bugly:crashreport:3.0.0' 44 | //leakcanary 45 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' 46 | releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' 47 | //mvp 48 | implementation 'com.github.cn-ljb:kotlin-mvp-lib:1.2.1' 49 | //net 50 | implementation 'com.github.cn-ljb:netlib:1.0.1' 51 | //dao 52 | implementation 'com.github.cn-ljb:daolib:1.0.1' 53 | //page layout 54 | implementation 'com.github.cn-ljb:PageStateLayout:1.0.0' 55 | //rx life 56 | implementation 'com.github.nekocode.rxlifecycle:rxlifecycle:2.0' 57 | implementation 'com.github.nekocode.rxlifecycle:rxlifecycle-compact:2.0' 58 | //gson 59 | implementation 'com.google.code.gson:gson:2.8.5' 60 | //glide 61 | implementation 'com.github.bumptech.glide:glide:4.9.0' 62 | implementation 'com.github.bumptech.glide:annotations:4.9.0' 63 | annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' 64 | implementation 'com.github.bumptech.glide:okhttp3-integration:4.7.1' 65 | implementation 'jp.wasabeef:glide-transformations:3.3.0' 66 | //smarttablayout: 67 | implementation 'com.ogaclejapan.smarttablayout:library:2.0.0@aar' 68 | } 69 | repositories { 70 | mavenCentral() 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | VERSION_DEBUG = true 3 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ljb/mvp/kotlin/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.ljb.mvp.kotlin", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/MVPKotlinApplication.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin 2 | 3 | import android.app.Application 4 | import com.ljb.mvp.kotlin.common.Constant 5 | import com.ljb.mvp.kotlin.common.Constant.GITHUB_CLIENT_ID 6 | import com.ljb.mvp.kotlin.common.Constant.GITHUB_CLIENT_SECRET 7 | import com.ljb.mvp.kotlin.common.Constant.HTTP_API_DOMAIN 8 | import com.ljb.mvp.kotlin.db.DatabaseHelperImpl 9 | import com.ljb.mvp.kotlin.protocol.dao.ProtocolConfig 10 | import com.ljb.mvp.kotlin.utils.SPUtils 11 | import com.squareup.leakcanary.LeakCanary 12 | import com.tencent.bugly.crashreport.CrashReport 13 | import dao.ljb.kt.DaoConfig 14 | import net.ljb.kt.HttpConfig 15 | 16 | /** 17 | * Created by L on 2017/7/14. 18 | */ 19 | class MVPKotlinApplication : Application() { 20 | 21 | //应避免创建全局的Application引用 22 | override fun onCreate() { 23 | super.onCreate() 24 | if (LeakCanary.isInAnalyzerProcess(this)) { 25 | return 26 | } 27 | LeakCanary.install(this) 28 | //bugly 29 | CrashReport.initCrashReport(applicationContext, Constant.APP_ID_BUGLY, BuildConfig.DEBUG) 30 | //SharedPreferences 31 | SPUtils.init(this) 32 | //初始化网络库 33 | initNet() 34 | //初始化数据库 35 | initDB() 36 | } 37 | 38 | private fun initDB() { 39 | val dbHelper = DatabaseHelperImpl(this) 40 | val protocolConfig = ProtocolConfig() 41 | DaoConfig.init(dbHelper, protocolConfig) 42 | } 43 | 44 | private fun initNet() { 45 | val paramMap = mapOf( 46 | "client_id" to GITHUB_CLIENT_ID, 47 | "client_secret" to GITHUB_CLIENT_SECRET 48 | ) 49 | HttpConfig.init(HTTP_API_DOMAIN, paramMap, paramMap, BuildConfig.DEBUG) 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/MainTabAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import android.widget.TextView 9 | import com.ljb.mvp.kotlin.R 10 | import com.ljb.mvp.kotlin.domain.TabBean 11 | import com.ljb.mvp.kotlin.widget.TabGroupView 12 | 13 | /** 14 | * Created by L on 2017/7/12. 15 | */ 16 | class MainTabAdapter(private val mContext: Context, val mData: List) : TabGroupView.TabAdapter { 17 | 18 | private val mLayoutInflater = LayoutInflater.from(mContext) 19 | 20 | override fun getCount() = mData.size 21 | 22 | override fun getTabView(position: Int, parent: ViewGroup?): View { 23 | val itemBean = mData[position] 24 | val view = mLayoutInflater.inflate(R.layout.layout_bottom_tab_defalut, parent, false) 25 | view.findViewById(R.id.bottom_tab_icon).setImageResource(itemBean.iconResID) 26 | view.findViewById(R.id.bottom_tab_text).setText(itemBean.textResID) 27 | return view 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/MyTabAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter 2 | 3 | import androidx.fragment.app.FragmentManager 4 | import androidx.fragment.app.FragmentPagerAdapter 5 | import com.ljb.mvp.kotlin.domain.MyTabFragmentBean 6 | 7 | /** 8 | * Created by L on 2017/7/19. 9 | */ 10 | class MyTabAdapter(fm: FragmentManager, private val mData: Array) : FragmentPagerAdapter(fm) { 11 | 12 | override fun getItem(position: Int) = mData[position].fragment 13 | 14 | override fun getCount() = mData.size 15 | 16 | override fun getPageTitle(position: Int) = mData[position].title 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/rv/EventAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter.rv 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.ljb.mvp.kotlin.R 9 | import com.ljb.mvp.kotlin.domain.Event 10 | import com.ljb.mvp.kotlin.domain.EventCommit 11 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 12 | import java.lang.StringBuilder 13 | 14 | /** 15 | * Created by L on 2017/7/19. 16 | */ 17 | class EventAdapter(mContext: Context, mData: MutableList) : LoadMoreRecyclerAdapter(mContext, mData) { 18 | 19 | override fun getItemHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder = 20 | EventViewHolder(mLayoutInflater.inflate(R.layout.item_event, parent, false)) 21 | 22 | override fun onBindData(holder: RecyclerView.ViewHolder, position: Int) { 23 | if (holder is EventViewHolder) { 24 | holder.tv_type.text = mData[position].type 25 | holder.tv_project_name.text = mData[position].repo.name 26 | holder.tv_url.text = mData[position].repo.url 27 | if (mData[position].payload.commits == null) { 28 | holder.tv_commit.visibility = View.VISIBLE 29 | if (mData[position].payload.action != null) { 30 | holder.tv_commit.text = mContext.getString(R.string.action, mData[position].payload.action) 31 | } else { 32 | holder.tv_commit.visibility = View.GONE 33 | } 34 | holder.tv_submitter.text = mData[position].actor.display_login 35 | } else { 36 | holder.tv_commit.visibility = View.VISIBLE 37 | holder.tv_commit.text = getCommitStr(mData[position].payload.commits!!) 38 | holder.tv_submitter.text = mData[position].payload.commits!![0].author.name 39 | } 40 | holder.tv_time.text = mData[position].created_at 41 | } 42 | } 43 | 44 | 45 | private fun getCommitStr(commits: List): String { 46 | return StringBuilder("commits:\n").apply { 47 | for ((index, comment) in commits.withIndex()) { 48 | if (index <= 3) { 49 | if (index != 0) { 50 | append("--- --- ---\n") 51 | } 52 | append("${comment.message}\n") 53 | } else { 54 | append("...") 55 | break 56 | } 57 | } 58 | }.toString() 59 | } 60 | 61 | 62 | override fun getItemCount(): Int = mData.size 63 | 64 | class EventViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 65 | val tv_type by lazy { itemView.findViewById(R.id.tv_type) } 66 | val tv_project_name by lazy { itemView.findViewById(R.id.tv_project_name) } 67 | val tv_url by lazy { itemView.findViewById(R.id.tv_url) } 68 | val tv_commit by lazy { itemView.findViewById(R.id.tv_commit) } 69 | val tv_submitter by lazy { itemView.findViewById(R.id.tv_submitter) } 70 | val tv_time by lazy { itemView.findViewById(R.id.tv_time) } 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/rv/FollowersAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter.rv 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.ljb.mvp.kotlin.R 10 | import com.ljb.mvp.kotlin.domain.Follower 11 | import com.ljb.mvp.kotlin.img.ImageLoader 12 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 13 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation 14 | 15 | /** 16 | * Created by L on 2017/7/19. 17 | */ 18 | class FollowersAdapter(mContext: Context, mData: MutableList) : LoadMoreRecyclerAdapter(mContext, mData) { 19 | 20 | 21 | override fun getItemHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder = 22 | FollowersViewHolder(mLayoutInflater.inflate(R.layout.item_followers, parent, false)) 23 | 24 | override fun onBindData(holder: RecyclerView.ViewHolder, position: Int) { 25 | if (holder is FollowersViewHolder) { 26 | val item = mData[position] 27 | ImageLoader.load(mContext, item.avatar_url, holder.iv_avatar, 28 | ImageLoader.getRoundRequest(10, RoundedCornersTransformation.CornerType.ALL)) 29 | holder.tv_follower_name.text = item.login 30 | } 31 | } 32 | 33 | 34 | class FollowersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 35 | val iv_avatar by lazy { itemView.findViewById(R.id.iv_avatar) } 36 | val tv_follower_name by lazy { itemView.findViewById(R.id.tv_follower_name) } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/rv/FollowersDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter.rv 2 | 3 | import android.graphics.Rect 4 | import android.view.View 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.ljb.mvp.kotlin.utils.UIUtils 7 | 8 | class FollowersDecoration : RecyclerView.ItemDecoration() { 9 | 10 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 11 | super.getItemOffsets(outRect, view, parent, state) 12 | outRect.top = UIUtils.dip2px(view.context, 2.5f) 13 | outRect.bottom = UIUtils.dip2px(view.context, 2.5f) 14 | outRect.left = UIUtils.dip2px(view.context, 5f) 15 | outRect.right = UIUtils.dip2px(view.context, 5f) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/rv/FollowingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter.rv 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.ljb.mvp.kotlin.R 10 | import com.ljb.mvp.kotlin.domain.Following 11 | import com.ljb.mvp.kotlin.img.ImageLoader 12 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 13 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation 14 | 15 | /** 16 | * Created by L on 2017/10/9. 17 | */ 18 | class FollowingAdapter(mContext: Context, mData: MutableList) : LoadMoreRecyclerAdapter(mContext, mData) { 19 | 20 | override fun getItemHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder = 21 | FollowingViewHolder(mLayoutInflater.inflate(R.layout.item_following, parent, false)) 22 | 23 | override fun onBindData(holder: RecyclerView.ViewHolder, position: Int) { 24 | if (holder is FollowingViewHolder) { 25 | val item = mData[position] 26 | holder.tv_following_name.text = item.login 27 | ImageLoader.load(mContext, item.avatar_url, holder.iv_avatar, 28 | ImageLoader.getRoundRequest(10, RoundedCornersTransformation.CornerType.ALL)) 29 | } 30 | } 31 | 32 | class FollowingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 33 | val tv_following_name by lazy { itemView.findViewById(R.id.tv_following_name) } 34 | val iv_avatar by lazy { itemView.findViewById(R.id.iv_avatar) } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/rv/RepositoriesAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter.rv 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.ljb.mvp.kotlin.R 9 | import com.ljb.mvp.kotlin.domain.Repository 10 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 11 | 12 | /** 13 | * Created by L on 2017/9/27. 14 | */ 15 | class RepositoriesAdapter(mContext: Context, mData: MutableList) : LoadMoreRecyclerAdapter(mContext, mData) { 16 | 17 | override fun getItemHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder = 18 | RepositoriesViewHolder(mLayoutInflater.inflate(R.layout.item_starred, parent, false)) 19 | 20 | override fun onBindData(holder: RecyclerView.ViewHolder, position: Int) { 21 | if (holder is RepositoriesViewHolder) { 22 | val item = mData[position] 23 | holder.tv_project_name.text = item.name 24 | holder.tv_language.text = item.language 25 | holder.tv_author.text = item.owner.login 26 | holder.tv_url.text = item.html_url 27 | holder.tv_star.text = mContext.getString(R.string.format_star, item.stargazers_count.toString()) 28 | holder.tv_fork.text = mContext.getString(R.string.format_fork, item.forks.toString()) 29 | holder.tv_issues.text = mContext.getString(R.string.format_issues, item.open_issues_count.toString()) 30 | holder.tv_update_time.text = mContext.getString(R.string.format_update, item.updated_at) 31 | holder.tv_create_time.text = mContext.getString(R.string.format_create, item.created_at) 32 | } 33 | } 34 | 35 | 36 | class RepositoriesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 37 | val tv_project_name by lazy { itemView.findViewById(R.id.tv_project_name) } 38 | val tv_language by lazy { itemView.findViewById(R.id.tv_language) } 39 | val tv_author by lazy { itemView.findViewById(R.id.tv_author) } 40 | val tv_url by lazy { itemView.findViewById(R.id.tv_url) } 41 | val tv_star by lazy { itemView.findViewById(R.id.tv_star) } 42 | val tv_fork by lazy { itemView.findViewById(R.id.tv_fork) } 43 | val tv_issues by lazy { itemView.findViewById(R.id.tv_issues) } 44 | val tv_update_time by lazy { itemView.findViewById(R.id.tv_update_time) } 45 | val tv_create_time by lazy { itemView.findViewById(R.id.tv_create_time) } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/adapter/rv/StarredAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.adapter.rv 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.ljb.mvp.kotlin.R 9 | import com.ljb.mvp.kotlin.domain.Starred 10 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 11 | 12 | /** 13 | * Created by L on 2017/7/19. 14 | */ 15 | class StarredAdapter(mContext: Context, mData: MutableList) : LoadMoreRecyclerAdapter(mContext, mData) { 16 | 17 | override fun getItemHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder = 18 | StarredViewHolder(mLayoutInflater.inflate(R.layout.item_starred, parent, false)) 19 | 20 | override fun onBindData(holder: RecyclerView.ViewHolder, position: Int) { 21 | if (holder is StarredViewHolder) { 22 | val item = mData[position] 23 | holder.tv_project_name.text = item.name 24 | holder.tv_language.text = item.language 25 | holder.tv_author.text = item.owner.login 26 | holder.tv_url.text = item.html_url 27 | holder.tv_star.text = mContext.getString(R.string.format_star, item.stargazers_count.toString()) 28 | holder.tv_fork.text = mContext.getString(R.string.format_fork, item.forks.toString()) 29 | holder.tv_issues.text = mContext.getString(R.string.format_issues, item.open_issues_count.toString()) 30 | holder.tv_update_time.text = mContext.getString(R.string.format_update, item.updated_at) 31 | holder.tv_create_time.text = mContext.getString(R.string.format_create, item.created_at) 32 | } 33 | } 34 | 35 | 36 | class StarredViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 37 | val tv_project_name by lazy { itemView.findViewById(R.id.tv_project_name) } 38 | val tv_language by lazy { itemView.findViewById(R.id.tv_language) } 39 | val tv_author by lazy { itemView.findViewById(R.id.tv_author) } 40 | val tv_url by lazy { itemView.findViewById(R.id.tv_url) } 41 | val tv_star by lazy { itemView.findViewById(R.id.tv_star) } 42 | val tv_fork by lazy { itemView.findViewById(R.id.tv_fork) } 43 | val tv_issues by lazy { itemView.findViewById(R.id.tv_issues) } 44 | val tv_update_time by lazy { itemView.findViewById(R.id.tv_update_time) } 45 | val tv_create_time by lazy { itemView.findViewById(R.id.tv_create_time) } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/Constant.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common 2 | 3 | import com.ljb.mvp.kotlin.BuildConfig 4 | 5 | /** 6 | * 常量池 7 | * Created by L on 2017/7/11. 8 | */ 9 | object Constant { 10 | 11 | const val APP_ID_BUGLY = "733873bc89" 12 | 13 | /** 服务器Host */ 14 | const val HTTP_API_DOMAIN = "https://api.github.com" 15 | 16 | /** GitHub接口身份验证 */ 17 | const val GITHUB_CLIENT_ID = "c68ea892fdebf06c418b" 18 | const val GITHUB_CLIENT_SECRET = "e2cf54c4dd27c702ca1d218ff87cbfc8fc6daccf" 19 | 20 | /** 21 | * SharedPreferences常量池 22 | * */ 23 | object SPKey { 24 | const val USER_LOGIN = "user_login" 25 | const val USER_NAME = "user_name" 26 | const val USER_ID = "user_id" 27 | const val USER_IMG = "user_img" 28 | } 29 | 30 | /** 31 | * 数据库常量池 32 | * */ 33 | object DB { 34 | const val NAME = "db_${BuildConfig.APPLICATION_ID}" 35 | const val VERSION = 1 36 | } 37 | 38 | /** 39 | * 权限Code 40 | * */ 41 | object PermissionCode { 42 | const val CODE_INIT = 0x00 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/LoginUser.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common 2 | 3 | import com.ljb.mvp.kotlin.utils.SPUtils 4 | 5 | /** 6 | * 用户登录信息 7 | * Author:Ljb 8 | * Time:2019/4/20 9 | * There is a lot of misery in life 10 | **/ 11 | object LoginUser { 12 | 13 | var uid: String 14 | get() = SPUtils.getString(Constant.SPKey.USER_ID) 15 | set(value) = SPUtils.putString(Constant.SPKey.USER_ID, value) 16 | 17 | var login: String 18 | get() = SPUtils.getString(Constant.SPKey.USER_LOGIN) 19 | set(value) = SPUtils.putString(Constant.SPKey.USER_LOGIN, value) 20 | 21 | var name: String 22 | get() = SPUtils.getString(Constant.SPKey.USER_NAME) 23 | set(value) = SPUtils.putString(Constant.SPKey.USER_NAME, value) 24 | 25 | var img: String 26 | get() = SPUtils.getString(Constant.SPKey.USER_IMG) 27 | set(value) = SPUtils.putString(Constant.SPKey.USER_IMG, value) 28 | 29 | fun clear() { 30 | login = "" 31 | uid = "" 32 | name = "" 33 | img = "" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/ex/BaseActivityLifecycleCallbacks.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common.ex 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | 7 | /** 8 | * Author:Ljb 9 | * Time:2019/3/22 10 | * There is a lot of misery in life 11 | **/ 12 | abstract class BaseActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks { 13 | 14 | override fun onActivityResumed(activity: Activity?) { 15 | } 16 | 17 | override fun onActivityStarted(activity: Activity?) { 18 | } 19 | 20 | override fun onActivityDestroyed(activity: Activity?) { 21 | } 22 | 23 | override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { 24 | } 25 | 26 | override fun onActivityStopped(activity: Activity?) { 27 | } 28 | 29 | override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { 30 | } 31 | 32 | override fun onActivityPaused(activity: Activity?) { 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/rx/BaseNetObserver.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common.rx 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | import com.ljb.mvp.kotlin.domain.GitHubHttpError 6 | import com.ljb.mvp.kotlin.utils.JsonParser 7 | 8 | import com.ljb.mvp.kotlin.utils.XLog 9 | 10 | import io.reactivex.Observer 11 | import io.reactivex.disposables.Disposable 12 | import retrofit2.HttpException 13 | 14 | /** 15 | * 网络请求公共代码 16 | */ 17 | open class BaseNetObserver(private val mContext: Context) : Observer { 18 | private var mOnNextEx: ((T) -> Unit)? = null 19 | private var mOnErrorEx: ((Throwable) -> Unit)? = null 20 | private var mOnCompleteEx: (() -> Unit)? = null 21 | private var mOnSubscribeEx: ((Disposable) -> Unit)? = null 22 | 23 | override fun onSubscribe(d: Disposable) { 24 | mOnSubscribeEx?.invoke(d) 25 | onSubscribeEx(d) 26 | } 27 | 28 | override fun onNext(response: T) { 29 | mOnNextEx?.invoke(response) 30 | onNextEx(response) 31 | } 32 | 33 | override fun onError(e: Throwable) { 34 | XLog.e(e) 35 | if (e is HttpException && e.response().errorBody() != null) { 36 | val errorJson = e.response().errorBody()!!.string() 37 | val gitHubHttpError = JsonParser.fromJsonObj(errorJson, GitHubHttpError::class.java) 38 | Toast.makeText(mContext, gitHubHttpError.message, Toast.LENGTH_SHORT).show() 39 | val newE = Throwable(gitHubHttpError.message) 40 | mOnErrorEx?.invoke(newE) 41 | onErrorEx(newE) 42 | return 43 | } 44 | mOnErrorEx?.invoke(e) 45 | onErrorEx(e) 46 | } 47 | 48 | override fun onComplete() { 49 | mOnCompleteEx?.invoke() 50 | onCompleteEx() 51 | } 52 | 53 | //以下方法提供匿名内部类的形式调用 54 | protected open fun onCompleteEx() { 55 | 56 | } 57 | 58 | protected open fun onSubscribeEx(d: Disposable) { 59 | 60 | } 61 | 62 | protected open fun onNextEx(data: T) { 63 | 64 | } 65 | 66 | protected open fun onErrorEx(e: Throwable) { 67 | 68 | } 69 | 70 | 71 | //以下方法提供Kotlin Lambda调用 72 | fun onErrorEx(error: (Throwable) -> Unit) { 73 | mOnErrorEx = error 74 | } 75 | 76 | fun onNextEx(next: (T) -> Unit) { 77 | mOnNextEx = next 78 | } 79 | 80 | fun onCompleteEx(complete: () -> Unit) { 81 | mOnCompleteEx = complete 82 | } 83 | 84 | fun onSubscribeEx(subscribe: (Disposable) -> Unit) { 85 | mOnSubscribeEx = subscribe 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/rx/BaseObserver.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common.rx 2 | 3 | 4 | import com.ljb.mvp.kotlin.utils.XLog 5 | 6 | import io.reactivex.Observer 7 | import io.reactivex.disposables.Disposable 8 | 9 | /** 10 | * 其他Rx订阅后公共代码 11 | * */ 12 | open class BaseObserver : Observer { 13 | private var mOnNextEx: ((T) -> Unit)? = null 14 | private var mOnErrorEx: ((Throwable) -> Unit)? = null 15 | private var mOnCompleteEx: (() -> Unit)? = null 16 | private var mOnSubscribeEx: ((Disposable) -> Unit)? = null 17 | 18 | override fun onSubscribe(d: Disposable) { 19 | mOnSubscribeEx?.invoke(d) 20 | onSubscribeEx(d) 21 | } 22 | 23 | override fun onNext(response: T) { 24 | mOnNextEx?.invoke(response) 25 | onNextEx(response) 26 | } 27 | 28 | override fun onError(e: Throwable) { 29 | XLog.e(e) 30 | mOnErrorEx?.invoke(e) 31 | onErrorEx(e) 32 | } 33 | 34 | override fun onComplete() { 35 | mOnCompleteEx?.invoke() 36 | onCompleteEx() 37 | } 38 | 39 | //以下方法提供匿名内部类的形式调用 40 | protected open fun onCompleteEx() { 41 | 42 | } 43 | 44 | protected open fun onSubscribeEx(d: Disposable) { 45 | 46 | } 47 | 48 | protected open fun onNextEx(data: T) { 49 | 50 | } 51 | 52 | protected open fun onErrorEx(e: Throwable) { 53 | 54 | } 55 | 56 | //以下代码提供Kotlin Lambda使用 57 | fun onErrorEx(error: (Throwable) -> Unit) { 58 | mOnErrorEx = error 59 | } 60 | 61 | fun onNextEx(next: (T) -> Unit) { 62 | mOnNextEx = next 63 | } 64 | 65 | fun onCompleteEx(complete: () -> Unit) { 66 | mOnCompleteEx = complete 67 | } 68 | 69 | fun onSubscribeEx(subscribe: (Disposable) -> Unit) { 70 | mOnSubscribeEx = subscribe 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/rx/RxEx.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common.rx 2 | 3 | import android.content.Context 4 | import io.reactivex.Observable 5 | 6 | /** 7 | * Author:Ljb 8 | * Time:2019/3/25 9 | * There is a lot of misery in life 10 | **/ 11 | fun Observable.subscribeEx(func: (BaseObserver.() -> Unit)) { 12 | subscribe(BaseObserver().apply(func)) 13 | } 14 | 15 | fun Observable.subscribeNet(context: Context, func: (BaseNetObserver.() -> Unit)) { 16 | subscribe(BaseNetObserver(context).apply(func)) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/common/rx/RxUtils.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.common.rx 2 | 3 | import android.app.Activity 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.fragment.app.Fragment 6 | import androidx.fragment.app.FragmentActivity 7 | import cn.nekocode.rxlifecycle.LifecycleEvent 8 | import cn.nekocode.rxlifecycle.RxLifecycle 9 | import cn.nekocode.rxlifecycle.compact.RxLifecycleCompact 10 | import io.reactivex.ObservableTransformer 11 | import io.reactivex.android.schedulers.AndroidSchedulers 12 | import io.reactivex.disposables.Disposable 13 | import io.reactivex.schedulers.Schedulers 14 | 15 | /** 16 | * Author:Ljb 17 | * Time:2018/12/28 18 | * There is a lot of misery in life 19 | **/ 20 | object RxUtils { 21 | 22 | /** 23 | * rx 取消订阅 24 | * */ 25 | fun dispose(disposable: Disposable?) { 26 | if (disposable != null && !disposable.isDisposed) { 27 | disposable.dispose() 28 | } 29 | } 30 | 31 | /** 32 | * rx 生命周期管理 33 | * */ 34 | fun bindToLifecycle(obj: Any): ObservableTransformer { 35 | return if (obj is AppCompatActivity) { 36 | RxLifecycleCompact.bind(obj).disposeObservableWhen(LifecycleEvent.DESTROY_VIEW) 37 | } else if (obj is FragmentActivity) { 38 | RxLifecycle.bind(obj).disposeObservableWhen(LifecycleEvent.DESTROY_VIEW) 39 | } else if (obj is Activity) { 40 | RxLifecycle.bind(obj).disposeObservableWhen(LifecycleEvent.DESTROY_VIEW) 41 | } else if (obj is Fragment) { 42 | RxLifecycleCompact.bind(obj).disposeObservableWhen(LifecycleEvent.DESTROY_VIEW) 43 | } else { 44 | throw IllegalArgumentException("obj isn't activity or fragment") 45 | } 46 | } 47 | 48 | /** 49 | * rx 线程调度 50 | * io -> android.main 51 | * */ 52 | fun schedulerIO2Main(): ObservableTransformer { 53 | return ObservableTransformer { upstream -> 54 | upstream.subscribeOn(Schedulers.io()) 55 | .observeOn(AndroidSchedulers.mainThread()) 56 | } 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/EventsContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import com.ljb.mvp.kotlin.contract.base.ListContract 4 | import com.ljb.mvp.kotlin.domain.Event 5 | import com.ljb.mvp.kotlin.domain.Repository 6 | import io.reactivex.Observable 7 | import mvp.ljb.kt.contract.IModelContract 8 | 9 | /** 10 | * @Author:Kotlin MVP Plugin 11 | * @Date:2019/04/20 12 | * @Description input description 13 | **/ 14 | interface EventsContract { 15 | 16 | interface IView : ListContract.IView { 17 | fun setRepos(repos: Repository?) 18 | } 19 | 20 | interface IPresenter : ListContract.IPresenter { 21 | fun getReposFromUrl(url: String) 22 | } 23 | 24 | interface IModel : IModelContract { 25 | fun getEvents(page: Int): Observable> 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/FollowersContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import com.ljb.mvp.kotlin.contract.base.ListContract 4 | import com.ljb.mvp.kotlin.domain.Follower 5 | import io.reactivex.Observable 6 | import mvp.ljb.kt.contract.IModelContract 7 | 8 | /** 9 | * @Author:Kotlin MVP Plugin 10 | * @Date:2019/04/20 11 | * @Description input description 12 | **/ 13 | interface FollowersContract { 14 | 15 | interface IView : ListContract.IView 16 | 17 | interface IPresenter : ListContract.IPresenter 18 | 19 | interface IModel : IModelContract { 20 | fun getFollowers(page: Int): Observable> 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/FollowingContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import com.ljb.mvp.kotlin.contract.base.ListContract 4 | import com.ljb.mvp.kotlin.domain.Following 5 | import io.reactivex.Observable 6 | import mvp.ljb.kt.contract.IModelContract 7 | 8 | /** 9 | * @Author:Kotlin MVP Plugin 10 | * @Date:2019/04/20 11 | * @Description input description 12 | **/ 13 | interface FollowingContract { 14 | 15 | interface IView : ListContract.IView 16 | 17 | interface IPresenter : ListContract.IPresenter 18 | 19 | interface IModel : IModelContract { 20 | fun getFollowing(page: Int): Observable> 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/HomeContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import mvp.ljb.kt.contract.IModelContract 4 | import mvp.ljb.kt.contract.IPresenterContract 5 | import mvp.ljb.kt.contract.IViewContract 6 | 7 | /** 8 | * @Author:Kotlin MVP Plugin 9 | * @Date:2019/04/20 10 | * @Description input description 11 | **/ 12 | interface HomeContract { 13 | 14 | interface IView : IViewContract 15 | 16 | interface IPresenter : IPresenterContract 17 | 18 | interface IModel : IModelContract 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/LoginContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import com.ljb.mvp.kotlin.domain.User 4 | import io.reactivex.Observable 5 | import mvp.ljb.kt.contract.IModelContract 6 | import mvp.ljb.kt.contract.IPresenterContract 7 | import mvp.ljb.kt.contract.IViewContract 8 | 9 | /** 10 | * @Author:Kotlin MVP Plugin 11 | * @Date:2019/04/20 12 | * @Description input description 13 | **/ 14 | interface LoginContract { 15 | 16 | interface IView : IViewContract { 17 | fun showLoadDialog() 18 | fun dismissLoadDialog() 19 | fun loginSuccess() 20 | fun loginError(errorMsg: String?) 21 | fun goHome() 22 | } 23 | 24 | interface IPresenter : IPresenterContract { 25 | fun login(userName: String) 26 | fun delayGoHomeTask() 27 | fun getLocLogin(): String? 28 | } 29 | 30 | interface IModel : IModelContract { 31 | fun delayGoHomeTask(): Observable 32 | fun getUserInfo(userName: String): Observable 33 | fun saveLoginUser2SP(user: User): User 34 | fun saveUser2DB(user: User): Observable 35 | fun getLocLogin(): String? 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/MyContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import mvp.ljb.kt.contract.IPresenterContract 4 | import mvp.ljb.kt.contract.IViewContract 5 | import com.ljb.mvp.kotlin.domain.User 6 | import io.reactivex.Observable 7 | import mvp.ljb.kt.contract.IModelContract 8 | 9 | /** 10 | * @Author:Kotlin MVP Plugin 11 | * @Date:2019/04/20 12 | * @Description input description 13 | **/ 14 | interface MyContract { 15 | 16 | interface IView : IViewContract { 17 | fun logoutSuccess() 18 | fun showUserInfo(user: User) 19 | } 20 | 21 | interface IPresenter : IPresenterContract { 22 | fun logout() 23 | fun getUserInfo() 24 | } 25 | 26 | interface IModel : IModelContract { 27 | fun getUserInfo(): Observable 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/RepositoriesContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import com.ljb.mvp.kotlin.contract.base.ListContract 4 | import com.ljb.mvp.kotlin.domain.Repository 5 | import io.reactivex.Observable 6 | import mvp.ljb.kt.contract.IModelContract 7 | 8 | /** 9 | * @Author:Kotlin MVP Plugin 10 | * @Date:2019/04/20 11 | * @Description input description 12 | **/ 13 | interface RepositoriesContract { 14 | 15 | interface IView : ListContract.IView 16 | 17 | interface IPresenter : ListContract.IPresenter 18 | 19 | interface IModel : IModelContract { 20 | fun getRepositories(page: Int): Observable> 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/StarredContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import com.ljb.mvp.kotlin.contract.base.ListContract 4 | import com.ljb.mvp.kotlin.domain.Starred 5 | import io.reactivex.Observable 6 | import mvp.ljb.kt.contract.IModelContract 7 | 8 | /** 9 | * @Author:Kotlin MVP Plugin 10 | * @Date:2019/04/20 11 | * @Description input description 12 | **/ 13 | interface StarredContract { 14 | 15 | interface IView : ListContract.IView 16 | 17 | interface IPresenter : ListContract.IPresenter 18 | 19 | interface IModel : IModelContract { 20 | fun getStarred(page: Int): Observable> 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/WebViewContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract 2 | 3 | import mvp.ljb.kt.contract.IModelContract 4 | import mvp.ljb.kt.contract.IPresenterContract 5 | import mvp.ljb.kt.contract.IViewContract 6 | 7 | /** 8 | * @Author:Kotlin MVP Plugin 9 | * @Date:2019/04/20 10 | * @Description input description 11 | **/ 12 | interface WebViewContract { 13 | 14 | interface IView : IViewContract 15 | 16 | interface IPresenter : IPresenterContract 17 | 18 | interface IModel : IModelContract 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/contract/base/ListContract.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.contract.base 2 | 3 | import mvp.ljb.kt.contract.IPresenterContract 4 | import mvp.ljb.kt.contract.IViewContract 5 | 6 | 7 | interface ListContract { 8 | 9 | interface IView : IViewContract { 10 | fun showPage(data: MutableList, page: Int) 11 | fun errorPage(t: Throwable, page: Int) 12 | } 13 | 14 | interface IPresenter : IPresenterContract { 15 | fun onRefresh() 16 | fun onLoadMore() 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/db/DatabaseHelperImpl.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.db 2 | 3 | import android.content.Context 4 | import android.database.sqlite.SQLiteDatabase 5 | import com.ljb.mvp.kotlin.common.Constant 6 | import com.ljb.mvp.kotlin.table.UserTable 7 | import com.ljb.mvp.kotlin.utils.XLog 8 | import dao.ljb.kt.core.DatabaseHelper 9 | 10 | /** 11 | * Author:Ljb 12 | * Time:2018/12/7 13 | * There is a lot of misery in life 14 | **/ 15 | class DatabaseHelperImpl(context: Context) : DatabaseHelper(context, Constant.DB.NAME, Constant.DB.VERSION) { 16 | 17 | override fun onCreate(db: SQLiteDatabase) { 18 | //数据库创建时,即可初始化用户信息表 19 | val userTable = UserTable() 20 | val sql = DatabaseSqlHelper.getCreateTableSql(userTable) 21 | db.execSQL(sql) 22 | } 23 | 24 | override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/db/DatabaseSqlHelper.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.db 2 | 3 | import android.content.ContentValues 4 | import android.database.Cursor 5 | import com.ljb.mvp.kotlin.domain.User 6 | import com.ljb.mvp.kotlin.table.UserTable 7 | import dao.ljb.kt.table.BaseTable 8 | 9 | /** 10 | * Author:Ljb 11 | * Time:2018/12/10 12 | * There is a lot of misery in life 13 | **/ 14 | object DatabaseSqlHelper { 15 | 16 | /** 17 | * 生成建表的sql语句的字段部分 18 | * @param table 19 | * @return sql 20 | */ 21 | fun getCreateTableSql(table: BaseTable): String { 22 | val tabColumns = table.getColumns() 23 | val iterator = tabColumns.entries.iterator() 24 | val columnsSql = StringBuilder().apply { 25 | append(" (") 26 | while (iterator.hasNext()) { 27 | val entry = iterator.next() 28 | val key = entry.key 29 | val value = entry.value 30 | append(" ").append(key).append(" ").append(value).append(", ") 31 | } 32 | delete(this.length - ", ".length, this.length) 33 | append(");") 34 | }.toString() 35 | return "create table if not exists ${table.getName()}$columnsSql" 36 | } 37 | 38 | fun getUser(cursor: Cursor, table: UserTable): User { 39 | val login = cursor.getString(cursor.getColumnIndex(table.COLUMN_LOGIN)) 40 | val userId = cursor.getString(cursor.getColumnIndex(table.COLUMN_USER_ID)) 41 | val avatar_url = cursor.getString(cursor.getColumnIndex(table.COLUMN_AVATAR_URL)) 42 | val name = cursor.getString(cursor.getColumnIndex(table.COLUMN_NAME)) 43 | val company = cursor.getString(cursor.getColumnIndex(table.COLUMN_COMPANY)) 44 | val blog = cursor.getString(cursor.getColumnIndex(table.COLUMN_BLOG)) 45 | val location = cursor.getString(cursor.getColumnIndex(table.COLUMN_LOCATION)) 46 | val email = cursor.getString(cursor.getColumnIndex(table.COLUMN_EMAIL)) 47 | val created_at = cursor.getString(cursor.getColumnIndex(table.COLUMN_CREATED_AT)) 48 | val updated_at = cursor.getString(cursor.getColumnIndex(table.COLUMN_UPDATED_AT)) 49 | return User(login, userId, avatar_url, name, company, blog, location, email, created_at, updated_at) 50 | } 51 | 52 | fun getUserContentValue(table: UserTable, user: User): ContentValues { 53 | val values = ContentValues() 54 | values.put(table.COLUMN_LOGIN, user.login) 55 | values.put(table.COLUMN_USER_ID, user.id) 56 | values.put(table.COLUMN_AVATAR_URL, user.avatar_url) 57 | values.put(table.COLUMN_NAME, user.name) 58 | values.put(table.COLUMN_COMPANY, user.company) 59 | values.put(table.COLUMN_BLOG, user.blog) 60 | values.put(table.COLUMN_LOCATION, user.location) 61 | values.put(table.COLUMN_EMAIL, user.email) 62 | values.put(table.COLUMN_CREATED_AT, user.created_at) 63 | values.put(table.COLUMN_UPDATED_AT, user.updated_at) 64 | return values 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/Event.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Created by L on 2017/9/14. 5 | */ 6 | data class Event( 7 | val id: String, 8 | val type: String, 9 | val actor: EventActor, 10 | val repo: EventRepo, 11 | val payload: EventPayLoad, 12 | val public: Boolean, 13 | val created_at: String 14 | ) 15 | 16 | 17 | data class EventRepo(val id: String, val name: String, val url: String) 18 | 19 | data class EventActor(val id: String, val login: String, val display_login: String, val gravatar_id: String, val url: String, val avatar_url: String) 20 | 21 | data class EventPayLoad(val action: String?, val push_id: String?, val size: Int?, val distinct_size: String?, val ref: String?, 22 | val head: String?, val before: String?, val commits: List?) 23 | 24 | data class EventCommit(val sha: String, val author: CommitAuthor, val message: String, val distinct: Boolean, val url: String) 25 | 26 | data class CommitAuthor(val email: String, val name: String) 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/Follower.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Created by L on 2017/9/22. 5 | */ 6 | class Follower(val login: String, val id: String, val avatar_url: String, val html_url: String) -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/Following.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Created by L on 2017/10/9. 5 | */ 6 | data class Following(val login: String, val avatar_url: String, val company: String, val location: String, val html_url: String) -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/GitHubHttpError.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Author:Ljb 5 | * Time:2019/4/20 6 | * There is a lot of misery in life 7 | **/ 8 | class GitHubHttpError(val message: String, val documentation_url: String) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/MyTabFragmentBean.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | 6 | /** 7 | * Created by L on 2017/7/19. 8 | */ 9 | data class MyTabFragmentBean(val title: String, val fragment: Fragment) -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Created by L on 2017/9/27. 5 | */ 6 | data class Repository(val id: String, val name: String, val full_name: String, val owner: Owner, val html_url: String, 7 | val stargazers_count: Int, val forks: Int, val open_issues_count: Int, 8 | val created_at: String, val updated_at: String, val language: String) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/Starred.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | 4 | /** 5 | * Created by L on 2017/9/21. 6 | */ 7 | data class Starred(val id: String, val name: String, val full_name: String, val owner: Owner, val html_url: String, 8 | val stargazers_count: Int, val forks: Int, val open_issues_count: Int, 9 | val created_at: String, val updated_at: String, val language: String) 10 | 11 | data class Owner(val login: String, val id: String, val avatar_url: String) -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/TabBean.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Created by L on 2017/7/18. 5 | */ 6 | data class TabBean(val iconResID: Int, val textResID: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/domain/User.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.domain 2 | 3 | /** 4 | * Created by L on 2017/7/17. 5 | */ 6 | data class User( 7 | val login: String, 8 | val id: String, 9 | val avatar_url: String, 10 | val name: String?, 11 | val company: String?, 12 | val blog: String?, 13 | val location: String?, 14 | val email: String?, 15 | val created_at: String, 16 | val updated_at: String? 17 | ) { 18 | companion object { 19 | val EMPTY = User("", "", "", "", "", "", "", "", "", "") 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/img/ImageLoader.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.img 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.graphics.Bitmap 6 | import android.graphics.drawable.Drawable 7 | import android.os.Build 8 | import android.os.Looper 9 | import android.text.TextUtils 10 | import android.widget.ImageView 11 | import com.bumptech.glide.Glide 12 | import com.bumptech.glide.load.engine.DiskCacheStrategy 13 | import com.bumptech.glide.request.RequestOptions 14 | import com.bumptech.glide.request.target.SimpleTarget 15 | import com.bumptech.glide.request.transition.Transition 16 | import com.ljb.mvp.kotlin.R 17 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation 18 | 19 | object ImageLoader { 20 | 21 | private val mDefRequestOptions = RequestOptions() 22 | .diskCacheStrategy(DiskCacheStrategy.ALL) 23 | .placeholder(R.color.bg_page) 24 | .error(R.color.bg_page) 25 | 26 | private val mCircleRequestOptions = RequestOptions 27 | .circleCropTransform() 28 | .diskCacheStrategy(DiskCacheStrategy.ALL) 29 | .placeholder(R.color.bg_page) 30 | .error(R.color.bg_page) 31 | 32 | 33 | fun getRoundRequest(radius: Int, type: RoundedCornersTransformation.CornerType): RequestOptions { 34 | return RequestOptions 35 | .bitmapTransform(RoundedCornersTransformation(radius, 0, type)) 36 | .diskCacheStrategy(DiskCacheStrategy.ALL) 37 | .placeholder(R.color.bg_page) 38 | .error(R.color.bg_page) 39 | } 40 | 41 | fun getCircleRequest(): RequestOptions { 42 | return mCircleRequestOptions 43 | } 44 | 45 | 46 | fun load(context: Context, url: String?, img: ImageView, 47 | request: RequestOptions = mDefRequestOptions) { 48 | if (url == null) return 49 | checkContext(context) 50 | val imgUrl = nvlUrl(url) 51 | Glide.with(context).load(imgUrl).apply(request).into(img) 52 | } 53 | 54 | 55 | fun load(context: Context, resId: Int?, img: ImageView, 56 | request: RequestOptions = mDefRequestOptions) { 57 | if (resId == null || resId == 0) return 58 | checkContext(context) 59 | Glide.with(context).load(resId).apply(request).into(img) 60 | } 61 | 62 | private fun checkContext(context: Context) { 63 | if (Thread.currentThread() != Looper.getMainLooper().thread) return 64 | if (context is Activity && (context.isFinishing || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && context.isDestroyed))) { 65 | return 66 | } 67 | } 68 | 69 | private fun nvlUrl(url: String): String { 70 | return if (!(url.startsWith("http", true) || url.startsWith("https", true))) { 71 | "http:$url" 72 | } else { 73 | url 74 | } 75 | } 76 | 77 | fun downImage(context: Context, url: String?, callBack: (bitmap: Bitmap?) -> Unit) { 78 | if (TextUtils.isEmpty(url)) { 79 | callBack.invoke(null) 80 | return 81 | } 82 | Glide.with(context).asBitmap().load(nvlUrl(url!!)).into(object : SimpleTarget() { 83 | 84 | override fun onLoadFailed(errorDrawable: Drawable?) = callBack.invoke(null) 85 | 86 | override fun onResourceReady(bitmap: Bitmap, transition: Transition?) = callBack.invoke(bitmap) 87 | 88 | }) 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/img/OkHttpAppGlideModule.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.img 2 | 3 | import android.content.Context 4 | import com.bumptech.glide.Glide 5 | import com.bumptech.glide.Registry 6 | import com.bumptech.glide.annotation.GlideModule 7 | import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader 8 | import com.bumptech.glide.load.model.GlideUrl 9 | import com.bumptech.glide.module.AppGlideModule 10 | import net.ljb.kt.client.HttpClient 11 | import java.io.InputStream 12 | 13 | @GlideModule 14 | class OkHttpAppGlideModule : AppGlideModule() { 15 | override fun registerComponents(context: Context, glide: Glide, registry: Registry) { 16 | registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(HttpClient.getHttpClient())) 17 | } 18 | 19 | override fun isManifestParsingEnabled(): Boolean { 20 | return false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/EventsModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.EventsContract 5 | import com.ljb.mvp.kotlin.domain.Event 6 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 7 | import io.reactivex.Observable 8 | import mvp.ljb.kt.model.BaseModel 9 | import net.ljb.kt.client.HttpFactory 10 | 11 | /** 12 | * @Author Kotlin MVP Plugin 13 | * @Date 2019/07/08 14 | * @Description input description 15 | **/ 16 | class EventsModel : BaseModel(), EventsContract.IModel { 17 | 18 | override fun getEvents(page: Int): Observable> { 19 | return HttpFactory.getProtocol(IUserHttpProtocol::class.java) 20 | .getEventsByName(LoginUser.login, page) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/FollowersModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.FollowersContract 5 | import com.ljb.mvp.kotlin.domain.Follower 6 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 7 | import io.reactivex.Observable 8 | import mvp.ljb.kt.model.BaseModel 9 | import net.ljb.kt.client.HttpFactory 10 | 11 | /** 12 | * @Author Kotlin MVP Plugin 13 | * @Date 2019/07/08 14 | * @Description input description 15 | **/ 16 | class FollowersModel : BaseModel(), FollowersContract.IModel { 17 | 18 | override fun getFollowers(page: Int): Observable> { 19 | return HttpFactory.getProtocol(IUserHttpProtocol::class.java) 20 | .getFollowersByName(LoginUser.login, page) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/FollowingModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.FollowingContract 5 | import com.ljb.mvp.kotlin.domain.Following 6 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 7 | import io.reactivex.Observable 8 | import mvp.ljb.kt.model.BaseModel 9 | import net.ljb.kt.client.HttpFactory 10 | 11 | /** 12 | * @Author Kotlin MVP Plugin 13 | * @Date 2019/07/08 14 | * @Description input description 15 | **/ 16 | class FollowingModel : BaseModel(), FollowingContract.IModel { 17 | 18 | override fun getFollowing(page: Int): Observable> { 19 | return HttpFactory.getProtocol(IUserHttpProtocol::class.java) 20 | .getFollowingByName(LoginUser.login, page) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/HomeModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.contract.HomeContract 4 | import mvp.ljb.kt.model.BaseModel 5 | 6 | /** 7 | * @Author Kotlin MVP Plugin 8 | * @Date 2019/07/08 9 | * @Description input description 10 | **/ 11 | class HomeModel : BaseModel(), HomeContract.IModel -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/LoginModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.LoginContract 5 | import com.ljb.mvp.kotlin.domain.User 6 | import com.ljb.mvp.kotlin.protocol.dao.IUserDaoProtocol 7 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 8 | import com.ljb.mvp.kotlin.table.UserTable 9 | import dao.ljb.kt.core.DaoFactory 10 | import io.reactivex.Observable 11 | import mvp.ljb.kt.model.BaseModel 12 | import mvp.ljb.kt.model.IBaseModel 13 | import net.ljb.kt.client.HttpFactory 14 | import java.util.concurrent.TimeUnit 15 | 16 | /** 17 | * @Author Kotlin MVP Plugin 18 | * @Date 2019/07/07 19 | * @Description input description 20 | **/ 21 | class LoginModel : BaseModel(), LoginContract.IModel { 22 | 23 | private val mUserTable = UserTable() 24 | 25 | override fun getLocLogin(): String? { 26 | return LoginUser.login 27 | } 28 | 29 | override fun getUserInfo(userName: String): Observable { 30 | return HttpFactory.getProtocol(IUserHttpProtocol::class.java) 31 | .getUserInfoByName(userName) 32 | } 33 | 34 | override fun saveLoginUser2SP(user: User): User { 35 | LoginUser.login = user.login 36 | LoginUser.uid = user.id 37 | LoginUser.name = user.name ?: "" 38 | LoginUser.img = user.avatar_url 39 | return user 40 | } 41 | 42 | override fun saveUser2DB(user: User): Observable { 43 | return DaoFactory.getProtocol(IUserDaoProtocol::class.java) 44 | .saveUser(mUserTable, user) 45 | } 46 | 47 | override fun delayGoHomeTask(): Observable { 48 | return Observable.timer(1500, TimeUnit.MILLISECONDS) 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/MyModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.MyContract 5 | import com.ljb.mvp.kotlin.domain.User 6 | import com.ljb.mvp.kotlin.protocol.dao.IUserDaoProtocol 7 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 8 | import com.ljb.mvp.kotlin.table.UserTable 9 | import dao.ljb.kt.core.DaoFactory 10 | import io.reactivex.Observable 11 | import mvp.ljb.kt.model.BaseModel 12 | import net.ljb.kt.client.HttpFactory 13 | 14 | /** 15 | * @Author Kotlin MVP Plugin 16 | * @Date 2019/07/08 17 | * @Description input description 18 | **/ 19 | class MyModel : BaseModel(), MyContract.IModel { 20 | 21 | private val mUserTable = UserTable() 22 | 23 | override fun getUserInfo(): Observable { 24 | return Observable.concat( 25 | DaoFactory.getProtocol(IUserDaoProtocol::class.java).queryUserByUserId(mUserTable, LoginUser.uid), 26 | HttpFactory.getProtocol(IUserHttpProtocol::class.java).getUserInfoByName(LoginUser.login) 27 | ).filter { User.EMPTY != it } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/RepositoriesModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.RepositoriesContract 5 | import com.ljb.mvp.kotlin.domain.Repository 6 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 7 | import io.reactivex.Observable 8 | import mvp.ljb.kt.model.BaseModel 9 | import net.ljb.kt.client.HttpFactory 10 | 11 | /** 12 | * @Author Kotlin MVP Plugin 13 | * @Date 2019/07/08 14 | * @Description input description 15 | **/ 16 | class RepositoriesModel : BaseModel(), RepositoriesContract.IModel { 17 | 18 | override fun getRepositories(page: Int): Observable> { 19 | return HttpFactory.getProtocol(IUserHttpProtocol::class.java) 20 | .getRepositoriesByName(LoginUser.login, page) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/StarredModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.contract.StarredContract 5 | import com.ljb.mvp.kotlin.domain.Starred 6 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 7 | import io.reactivex.Observable 8 | import mvp.ljb.kt.model.BaseModel 9 | import net.ljb.kt.client.HttpFactory 10 | 11 | /** 12 | * @Author Kotlin MVP Plugin 13 | * @Date 2019/07/08 14 | * @Description input description 15 | **/ 16 | class StarredModel : BaseModel(), StarredContract.IModel { 17 | 18 | override fun getStarred(page: Int): Observable> { 19 | return HttpFactory.getProtocol(IUserHttpProtocol::class.java) 20 | .getStarredByName(LoginUser.login, page) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/model/WebViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.model 2 | 3 | import com.ljb.mvp.kotlin.contract.WebViewContract 4 | import mvp.ljb.kt.model.BaseModel 5 | 6 | /** 7 | * @Author Kotlin MVP Plugin 8 | * @Date 2019/07/08 9 | * @Description input description 10 | **/ 11 | class WebViewModel : BaseModel(), WebViewContract.IModel -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/EventsPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.rx.RxUtils 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.EventsContract 6 | import com.ljb.mvp.kotlin.domain.Event 7 | import com.ljb.mvp.kotlin.model.EventsModel 8 | import com.ljb.mvp.kotlin.protocol.http.IReposHttpProtocol 9 | import io.reactivex.android.schedulers.AndroidSchedulers 10 | import io.reactivex.schedulers.Schedulers 11 | import mvp.ljb.kt.presenter.BaseMvpPresenter 12 | import mvp.ljb.kt.presenter.getContextEx 13 | import net.ljb.kt.client.HttpFactory 14 | 15 | /** 16 | * @Author:Kotlin MVP Plugin 17 | * @Date:2019/04/20 18 | * @Description input description 19 | **/ 20 | class EventsPresenter : BaseMvpPresenter(), EventsContract.IPresenter { 21 | 22 | override fun registerModel() = EventsModel::class.java 23 | 24 | private var mPage = 1 25 | 26 | override fun onLoadMore() { 27 | getDataFromNet(mPage) 28 | } 29 | 30 | override fun onRefresh() { 31 | mPage = 1 32 | getDataFromNet(mPage) 33 | } 34 | 35 | private fun getDataFromNet(page: Int) { 36 | getModel().getEvents(page) 37 | .compose(RxUtils.bindToLifecycle(getMvpView())) 38 | .compose(RxUtils.schedulerIO2Main>()) 39 | .subscribeNet(getContextEx()) { 40 | onNextEx { 41 | getMvpView().showPage(it, page) 42 | mPage++ 43 | } 44 | onErrorEx { getMvpView().errorPage(it, page) } 45 | } 46 | } 47 | 48 | override fun getReposFromUrl(url: String) { 49 | HttpFactory.getProtocol(IReposHttpProtocol::class.java) 50 | .getReposFromUrl(url) 51 | .compose(RxUtils.bindToLifecycle(getMvpView())) 52 | .subscribeOn(Schedulers.io()) 53 | .observeOn(AndroidSchedulers.mainThread()) 54 | .subscribeNet(getContextEx()) { 55 | onNextEx { getMvpView().setRepos(it) } 56 | onErrorEx { getMvpView().setRepos(null) } 57 | } 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/FollowersPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.rx.RxUtils 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.FollowersContract 6 | import com.ljb.mvp.kotlin.domain.Follower 7 | import com.ljb.mvp.kotlin.model.FollowersModel 8 | import mvp.ljb.kt.presenter.BaseMvpPresenter 9 | import mvp.ljb.kt.presenter.getContextEx 10 | 11 | /** 12 | * @Author:Kotlin MVP Plugin 13 | * @Date:2019/04/20 14 | * @Description input description 15 | **/ 16 | class FollowersPresenter : BaseMvpPresenter(), FollowersContract.IPresenter { 17 | 18 | private var mPage = 1 19 | 20 | override fun registerModel() = FollowersModel::class.java 21 | 22 | override fun onLoadMore() { 23 | getDataFromNet(mPage) 24 | } 25 | 26 | override fun onRefresh() { 27 | mPage = 1 28 | getDataFromNet(mPage) 29 | } 30 | 31 | private fun getDataFromNet(page: Int) { 32 | getModel().getFollowers(page) 33 | .compose(RxUtils.bindToLifecycle(getMvpView())) 34 | .compose(RxUtils.schedulerIO2Main>()) 35 | .subscribeNet(getContextEx()) { 36 | onNextEx { 37 | getMvpView().showPage(it, page) 38 | mPage++ 39 | } 40 | onErrorEx { getMvpView().errorPage(it, page) } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/FollowingPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.rx.RxUtils 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.FollowingContract 6 | import com.ljb.mvp.kotlin.model.FollowingModel 7 | import mvp.ljb.kt.presenter.BaseMvpPresenter 8 | import mvp.ljb.kt.presenter.getContextEx 9 | 10 | /** 11 | * @Author:Kotlin MVP Plugin 12 | * @Date:2019/04/20 13 | * @Description input description 14 | **/ 15 | class FollowingPresenter : BaseMvpPresenter(), FollowingContract.IPresenter { 16 | 17 | override fun registerModel() = FollowingModel::class.java 18 | 19 | private var mPage = 1 20 | 21 | override fun onLoadMore() { 22 | getDataFromNet(mPage) 23 | } 24 | 25 | override fun onRefresh() { 26 | mPage = 1 27 | getDataFromNet(mPage) 28 | } 29 | 30 | private fun getDataFromNet(page: Int) { 31 | getModel().getFollowing(page) 32 | .compose(RxUtils.bindToLifecycle(getMvpView())) 33 | .compose(RxUtils.schedulerIO2Main()) 34 | .subscribeNet(getContextEx()) { 35 | onNextEx { 36 | getMvpView().showPage(it, page) 37 | mPage++ 38 | } 39 | onErrorEx { getMvpView().errorPage(it, page) } 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/HomePresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.contract.HomeContract 4 | import com.ljb.mvp.kotlin.model.HomeModel 5 | import mvp.ljb.kt.presenter.BaseMvpPresenter 6 | 7 | /** 8 | * @Author:Kotlin MVP Plugin 9 | * @Date:2019/04/20 10 | * @Description input description 11 | **/ 12 | class HomePresenter : BaseMvpPresenter(), HomeContract.IPresenter { 13 | 14 | override fun registerModel() = HomeModel::class.java 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/LoginPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.rx.subscribeEx 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.LoginContract 6 | import com.ljb.mvp.kotlin.model.LoginModel 7 | import com.ljb.mvp.kotlin.table.UserTable 8 | import com.ljb.mvp.kotlin.common.rx.RxUtils 9 | import io.reactivex.android.schedulers.AndroidSchedulers 10 | import io.reactivex.schedulers.Schedulers 11 | import mvp.ljb.kt.presenter.BaseMvpPresenter 12 | import mvp.ljb.kt.presenter.getContextEx 13 | 14 | /** 15 | * @Author:Kotlin MVP Plugin 16 | * @Date:2019/04/20 17 | * @Description input description 18 | **/ 19 | class LoginPresenter : BaseMvpPresenter(), LoginContract.IPresenter { 20 | 21 | override fun registerModel() = LoginModel::class.java 22 | 23 | override fun getLocLogin(): String? { 24 | return getModel().getLocLogin() 25 | } 26 | 27 | override fun delayGoHomeTask() { 28 | getModel().delayGoHomeTask() 29 | .compose(RxUtils.bindToLifecycle(getMvpView())) 30 | .observeOn(AndroidSchedulers.mainThread()) 31 | .subscribeEx { 32 | onNextEx { getMvpView().goHome() } 33 | } 34 | } 35 | 36 | override fun login(userName: String) { 37 | getModel().getUserInfo(userName) 38 | .map { getModel().saveLoginUser2SP(it) } 39 | .flatMap { getModel().saveUser2DB(it) } 40 | .compose(RxUtils.bindToLifecycle(getMvpView())) 41 | .compose(RxUtils.schedulerIO2Main()) 42 | .subscribeNet(getContextEx()) { 43 | onSubscribeEx { 44 | getMvpView().showLoadDialog() 45 | } 46 | onNextEx { 47 | getMvpView().dismissLoadDialog() 48 | getMvpView().loginSuccess() 49 | } 50 | onErrorEx { 51 | getMvpView().dismissLoadDialog() 52 | getMvpView().loginError(it.message) 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/MyPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.LoginUser 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.MyContract 6 | import com.ljb.mvp.kotlin.domain.User 7 | import com.ljb.mvp.kotlin.protocol.dao.IUserDaoProtocol 8 | import com.ljb.mvp.kotlin.protocol.http.IUserHttpProtocol 9 | import com.ljb.mvp.kotlin.table.UserTable 10 | import com.ljb.mvp.kotlin.common.rx.RxUtils 11 | import com.ljb.mvp.kotlin.model.MyModel 12 | import dao.ljb.kt.core.DaoFactory 13 | import io.reactivex.Observable 14 | import io.reactivex.android.schedulers.AndroidSchedulers 15 | import io.reactivex.schedulers.Schedulers 16 | import mvp.ljb.kt.presenter.BaseMvpPresenter 17 | import mvp.ljb.kt.presenter.getContextEx 18 | import net.ljb.kt.client.HttpFactory 19 | 20 | /** 21 | * @Author:Kotlin MVP Plugin 22 | * @Date:2019/04/20 23 | * @Description input description 24 | **/ 25 | class MyPresenter : BaseMvpPresenter(), MyContract.IPresenter { 26 | 27 | override fun registerModel() = MyModel::class.java 28 | 29 | override fun getUserInfo() { 30 | getModel().getUserInfo() 31 | .compose(RxUtils.bindToLifecycle(getMvpView())) 32 | .compose(RxUtils.schedulerIO2Main()) 33 | .subscribeNet(getContextEx()) { 34 | onNextEx { getMvpView().showUserInfo(it) } 35 | } 36 | } 37 | 38 | override fun logout() { 39 | LoginUser.clear() 40 | getMvpView().logoutSuccess() 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/RepositoriesPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.rx.RxUtils 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.RepositoriesContract 6 | import com.ljb.mvp.kotlin.domain.Repository 7 | import com.ljb.mvp.kotlin.model.RepositoriesModel 8 | import mvp.ljb.kt.presenter.BaseMvpPresenter 9 | import mvp.ljb.kt.presenter.getContextEx 10 | 11 | /** 12 | * @Author:Kotlin MVP Plugin 13 | * @Date:2019/04/20 14 | * @Description input description 15 | **/ 16 | class RepositoriesPresenter : BaseMvpPresenter(), RepositoriesContract.IPresenter { 17 | 18 | private var mPage = 1 19 | 20 | override fun registerModel() = RepositoriesModel::class.java 21 | 22 | override fun onLoadMore() { 23 | getDataFromNet(mPage) 24 | } 25 | 26 | override fun onRefresh() { 27 | mPage = 1 28 | getDataFromNet(mPage) 29 | } 30 | 31 | private fun getDataFromNet(page: Int) { 32 | getModel().getRepositories(page) 33 | .compose(RxUtils.bindToLifecycle(getMvpView())) 34 | .compose(RxUtils.schedulerIO2Main>()) 35 | .subscribeNet(getContextEx()) { 36 | onNextEx { 37 | getMvpView().showPage(it, page) 38 | mPage++ 39 | } 40 | onErrorEx { getMvpView().errorPage(it, page) } 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/StarredPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.common.rx.RxUtils 4 | import com.ljb.mvp.kotlin.common.rx.subscribeNet 5 | import com.ljb.mvp.kotlin.contract.StarredContract 6 | import com.ljb.mvp.kotlin.domain.Starred 7 | import com.ljb.mvp.kotlin.model.StarredModel 8 | import mvp.ljb.kt.presenter.BaseMvpPresenter 9 | import mvp.ljb.kt.presenter.getContextEx 10 | 11 | /** 12 | * @Author:Kotlin MVP Plugin 13 | * @Date:2019/04/20 14 | * @Description input description 15 | **/ 16 | class StarredPresenter : BaseMvpPresenter(), StarredContract.IPresenter { 17 | 18 | override fun registerModel() = StarredModel::class.java 19 | 20 | private var mPage = 1 21 | 22 | override fun onLoadMore() { 23 | getDataFromNet(mPage) 24 | } 25 | 26 | override fun onRefresh() { 27 | mPage = 1 28 | getDataFromNet(mPage) 29 | } 30 | 31 | private fun getDataFromNet(page: Int) { 32 | getModel().getStarred(page) 33 | .compose(RxUtils.bindToLifecycle(getMvpView())) 34 | .compose(RxUtils.schedulerIO2Main>()) 35 | .subscribeNet(getContextEx()) { 36 | onNextEx { 37 | getMvpView().showPage(it, page) 38 | mPage++ 39 | } 40 | onErrorEx { getMvpView().errorPage(it, page) } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/presenter/WebViewPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.presenter 2 | 3 | import com.ljb.mvp.kotlin.contract.WebViewContract 4 | import com.ljb.mvp.kotlin.model.WebViewModel 5 | import mvp.ljb.kt.presenter.BaseMvpPresenter 6 | 7 | /** 8 | * @Author:Kotlin MVP Plugin 9 | * @Date:2019/04/20 10 | * @Description input description 11 | **/ 12 | class WebViewPresenter : BaseMvpPresenter(), WebViewContract.IPresenter { 13 | 14 | override fun registerModel() = WebViewModel::class.java 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/protocol/dao/IUserDaoProtocol.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.protocol.dao 2 | 3 | import com.ljb.mvp.kotlin.domain.User 4 | import com.ljb.mvp.kotlin.table.UserTable 5 | import dao.ljb.kt.core.IDaoInterface 6 | import io.reactivex.Observable 7 | 8 | /** 9 | * Author:Ljb 10 | * Time:2019/4/20 11 | * There is a lot of misery in life 12 | **/ 13 | interface IUserDaoProtocol : IDaoInterface { 14 | /** 15 | * 通过用户id查询用户信息 16 | * */ 17 | fun queryUserByUserId(table: UserTable, userId: String): Observable 18 | 19 | /** 20 | * 保存用户 21 | * */ 22 | fun saveUser(table: UserTable, user: User): Observable 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/protocol/dao/ProtocolConfig.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.protocol.dao 2 | 3 | import com.ljb.mvp.kotlin.protocol.dao.impl.UserDaoProtocol 4 | import dao.ljb.kt.core.IDaoProtocolConfig 5 | 6 | /** 7 | * Author:Ljb 8 | * Time:2019/3/25 9 | * There is a lot of misery in life 10 | **/ 11 | class ProtocolConfig : IDaoProtocolConfig { 12 | 13 | @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY") 14 | override fun transformProtocol(clazz: Class) = when (clazz) { 15 | IUserDaoProtocol::class.java -> UserDaoProtocol() 16 | else -> throw IllegalStateException("not found dao interface object : ${clazz.name}") 17 | } as T 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/protocol/dao/impl/UserDaoProtocol.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.protocol.dao.impl 2 | 3 | import android.database.Cursor 4 | import com.ljb.mvp.kotlin.db.DatabaseSqlHelper 5 | import com.ljb.mvp.kotlin.domain.User 6 | import com.ljb.mvp.kotlin.protocol.dao.IUserDaoProtocol 7 | import com.ljb.mvp.kotlin.table.UserTable 8 | import com.ljb.mvp.kotlin.utils.XLog 9 | import dao.ljb.kt.core.BaseDaoProtocol 10 | import io.reactivex.Observable 11 | 12 | /** 13 | * Author:Ljb 14 | * Time:2019/4/20 15 | * There is a lot of misery in life 16 | **/ 17 | class UserDaoProtocol : BaseDaoProtocol(), IUserDaoProtocol { 18 | 19 | override fun queryUserByUserId(table: UserTable, userId: String): Observable = createObservable { 20 | queryUserByUserIdImpl(table, userId) ?: User.EMPTY 21 | } 22 | 23 | override fun saveUser(table: UserTable, user: User): Observable = createObservable { 24 | saveUserImpl(table, user) 25 | } 26 | 27 | private fun saveUserImpl(table: UserTable, user: User): Boolean { 28 | val result: Boolean 29 | val dbUser = queryUserByUserIdImpl(table, user.id) 30 | if (dbUser == null) { 31 | val insert = insertUserImpl(table, user) 32 | result = insert != -1L 33 | } else { 34 | val update = updateUserImpl(table, user) 35 | result = update > 0 36 | } 37 | return result 38 | } 39 | 40 | private fun updateUserImpl(table: UserTable, user: User): Int { 41 | var result = 0 42 | try { 43 | val contentValue = DatabaseSqlHelper.getUserContentValue(table, user) 44 | result = mSqliteDb.update(table.getName(), contentValue, "${table.COLUMN_USER_ID} = ?", arrayOf(user.id)) 45 | } catch (e: Exception) { 46 | XLog.e(e) 47 | } 48 | return result 49 | } 50 | 51 | private fun insertUserImpl(table: UserTable, user: User): Long { 52 | var result = -1L 53 | try { 54 | val contentValue = DatabaseSqlHelper.getUserContentValue(table, user) 55 | result = mSqliteDb.insert(table.getName(), null, contentValue) 56 | } catch (e: Exception) { 57 | XLog.e(e) 58 | } 59 | return result 60 | } 61 | 62 | private fun queryUserByUserIdImpl(table: UserTable, userId: String): User? { 63 | var user: User? = null 64 | var cursor: Cursor? = null 65 | try { 66 | cursor = mSqliteDb.rawQuery("select * from ${table.getName()} where ${table.COLUMN_USER_ID}= ?", arrayOf(userId)) 67 | if (cursor != null && cursor.moveToNext()) { 68 | user = DatabaseSqlHelper.getUser(cursor, table) 69 | } 70 | } catch (e: Exception) { 71 | XLog.e(e) 72 | } finally { 73 | cursor?.close() 74 | } 75 | return user 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/protocol/http/IReposHttpProtocol.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.protocol.http 2 | 3 | import com.ljb.mvp.kotlin.domain.Repository 4 | import com.ljb.mvp.kotlin.domain.User 5 | import io.reactivex.Observable 6 | import retrofit2.http.GET 7 | import retrofit2.http.Path 8 | import retrofit2.http.Url 9 | 10 | /** 11 | * Author:Ljb 12 | * Time:2018/8/15 13 | * There is a lot of misery in life 14 | **/ 15 | 16 | interface IReposHttpProtocol { 17 | /** 18 | * @param url ReposUrl 19 | * @return Repos信息 20 | * */ 21 | @GET 22 | fun getReposFromUrl(@Url url: String): Observable 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/protocol/http/IUserHttpProtocol.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.protocol.http 2 | 3 | import com.ljb.mvp.kotlin.domain.* 4 | import io.reactivex.Observable 5 | import retrofit2.http.GET 6 | import retrofit2.http.Path 7 | import retrofit2.http.Query 8 | 9 | interface IUserHttpProtocol { 10 | /** 11 | * 通过用户名获取用户信息 12 | * @param userName 用户名 13 | * @return 用户基本信息 14 | * */ 15 | @GET("/users/{userName}") 16 | fun getUserInfoByName(@Path("userName") userName: String): Observable 17 | 18 | /** 19 | * @param userName 用户名 20 | * @param page 页码 21 | * @return Events列表数据 22 | * */ 23 | @GET("/users/{userName}/events") 24 | fun getEventsByName(@Path("userName") userName: String, @Query("page") page: Int): Observable> 25 | 26 | /** 27 | * @param userName 用户名 28 | * @param page 页码 29 | * @return Starred列表数据 30 | * */ 31 | @GET("/users/{userName}/starred") 32 | fun getStarredByName(@Path("userName") userName: String, @Query("page") page: Int): Observable> 33 | 34 | /** 35 | * @param userName 用户名 36 | * @param page 页码 37 | * @returnt Followers列表数据 38 | * */ 39 | @GET("/users/{userName}/followers") 40 | fun getFollowersByName(@Path("userName") userName: String, @Query("page") page: Int): Observable> 41 | 42 | /** 43 | * @param userName 用户名 44 | * @param page 页码 45 | * @returnt Repositories列表数据 46 | * */ 47 | @GET("/users/{userName}/repos") 48 | fun getRepositoriesByName(@Path("userName") userName: String, @Query("page") page: Int): Observable> 49 | 50 | /** 51 | * @param userName 用户名 52 | * @param page 页码 53 | * @returnt Following列表数据 54 | * */ 55 | @GET("/users/{userName}/following") 56 | fun getFollowingByName(@Path("userName") userName: String, @Query("page") page: Int): Observable> 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/table/UserTable.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.table 2 | 3 | import android.provider.BaseColumns 4 | import dao.ljb.kt.table.BaseTable 5 | 6 | /** 7 | * Author:Ljb 8 | * Time:2019/4/20 9 | * There is a lot of misery in life 10 | **/ 11 | class UserTable : BaseTable() { 12 | 13 | val COLUMN_ID = BaseColumns._ID 14 | val COLUMN_LOGIN = "login" 15 | val COLUMN_USER_ID = "user_id" 16 | val COLUMN_AVATAR_URL = "avatar_url" 17 | val COLUMN_NAME = "name" 18 | val COLUMN_COMPANY = "company" 19 | val COLUMN_BLOG = "blog" 20 | val COLUMN_LOCATION = "location" 21 | val COLUMN_EMAIL = "email" 22 | val COLUMN_CREATED_AT = "created_at" 23 | val COLUMN_UPDATED_AT = "updated_at" 24 | 25 | override fun createTableName() = "tb_user" 26 | 27 | override fun createColumns(): Map { 28 | val tableColumns = HashMap() 29 | tableColumns[COLUMN_ID] = "integer primary key autoincrement" 30 | tableColumns[COLUMN_LOGIN] = TYPE_TEXT 31 | tableColumns[COLUMN_USER_ID] = TYPE_TEXT 32 | tableColumns[COLUMN_AVATAR_URL] = TYPE_TEXT 33 | tableColumns[COLUMN_NAME] = TYPE_TEXT 34 | tableColumns[COLUMN_COMPANY] = TYPE_TEXT 35 | tableColumns[COLUMN_BLOG] = TYPE_TEXT 36 | tableColumns[COLUMN_LOCATION] = TYPE_TEXT 37 | tableColumns[COLUMN_EMAIL] = TYPE_TEXT 38 | tableColumns[COLUMN_CREATED_AT] = TYPE_TEXT 39 | tableColumns[COLUMN_UPDATED_AT] = TYPE_TEXT 40 | return tableColumns 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/utils/JsonParser.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.utils 2 | 3 | import com.google.gson.Gson 4 | 5 | /** 6 | * Created by L on 2017/7/17. 7 | */ 8 | object JsonParser { 9 | 10 | private val mGson by lazy { Gson() } 11 | private val googleJsonParser by lazy { com.google.gson.JsonParser() } 12 | 13 | 14 | fun fromJsonObj(json: String, clazz: Class): T = mGson.fromJson(json, clazz) 15 | 16 | fun fromJsonArr(json: String, clazz: Class): MutableList { 17 | val result = ArrayList() 18 | val jsonArray = googleJsonParser.parse(json).asJsonArray 19 | jsonArray.mapTo(result) { mGson.fromJson(it, clazz) } 20 | return result 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/utils/PermissionUtils.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.utils 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.pm.PackageManager 6 | import android.os.Build 7 | import androidx.core.app.ActivityCompat 8 | import java.util.* 9 | import kotlin.collections.set 10 | 11 | /** 12 | * Author:Ljb 13 | * Time:2018/12/28 14 | * There is a lot of misery in life 15 | **/ 16 | object PermissionUtils { 17 | 18 | private val mCallBackMap: WeakHashMap, grantResults: IntArray) -> Unit> = WeakHashMap() 19 | 20 | fun checkPermission(context: Context, permission: String): Boolean { 21 | return ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED 22 | } 23 | 24 | /** 25 | * 必须覆写Activity#onRequestPermissionsResult 26 | * 并调用 PermissionUtils#onRequestPermissionsResult(int, String[], int[]) 27 | * @param act Activity 28 | * @param requestCode 取值范围(0-255) 29 | */ 30 | fun requestPermission(act: Activity, permissions: Array, requestCode: Int, callBack: (permissions: Array, grantResults: IntArray) -> Unit) { 31 | mCallBackMap[requestCode] = callBack 32 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 33 | val listCallPermission = ArrayList() 34 | for (permission in permissions) { 35 | if (!checkPermission(act, permission)) { 36 | listCallPermission.add(permission) 37 | } 38 | } 39 | if (listCallPermission.size == 0) { 40 | doAllPermissionGranted(permissions, requestCode) 41 | } else { 42 | ActivityCompat.requestPermissions(act, permissions, requestCode) 43 | } 44 | } else { 45 | doAllPermissionGranted(permissions, requestCode) 46 | } 47 | } 48 | 49 | /** 50 | * 检测所有的权限结果 51 | * 只要有一个未授权 返回false 52 | * */ 53 | fun checkAllResult(resultArr: IntArray): Boolean { 54 | for (result in resultArr) { 55 | if (result != PackageManager.PERMISSION_GRANTED) { 56 | return false 57 | } 58 | } 59 | return true 60 | } 61 | 62 | private fun doAllPermissionGranted(permissions: Array, requestCode: Int) { 63 | val grantResults = IntArray(permissions.size) 64 | for (i in grantResults.indices) { 65 | grantResults[i] = PackageManager.PERMISSION_GRANTED 66 | } 67 | onRequestPermissionsResult(requestCode, permissions, grantResults) 68 | } 69 | 70 | 71 | fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 72 | if (permissions.isEmpty() || grantResults.isEmpty()) { 73 | mCallBackMap.remove(requestCode) 74 | return 75 | } 76 | mCallBackMap[requestCode]?.invoke(permissions, grantResults) 77 | mCallBackMap.remove(requestCode) 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/utils/SPUtils.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | import com.ljb.mvp.kotlin.BuildConfig 7 | 8 | /** 9 | * Author:Ljb 10 | * Time:2018/12/28 11 | * There is a lot of misery in life 12 | **/ 13 | object SPUtils { 14 | 15 | private const val SP_NAME = "sp_${BuildConfig.APPLICATION_ID}" 16 | 17 | private var instance: SharedPreferences? = null 18 | 19 | /** 使用前请先初始化 */ 20 | fun init(context: Context) { 21 | if (instance != null) return 22 | instance = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE) 23 | } 24 | 25 | private fun checkInstance() { 26 | if (instance == null) throw InstantiationException("not init SharedPreferences ") 27 | } 28 | 29 | fun putString(key: String, value: String) { 30 | checkInstance() 31 | instance!!.edit().putString(key, value).apply() 32 | } 33 | 34 | @SuppressLint("ApplySharedPref") 35 | fun putStringCommit(key: String, value: String) { 36 | checkInstance() 37 | instance!!.edit().putString(key, value).commit() 38 | } 39 | 40 | 41 | fun getString(key: String, def: String = ""): String { 42 | checkInstance() 43 | return instance!!.getString(key, def) 44 | } 45 | 46 | fun putBoolean(key: String, value: Boolean) { 47 | checkInstance() 48 | instance!!.edit().putBoolean(key, value).apply() 49 | } 50 | 51 | fun getBoolean(key: String, def: Boolean = false): Boolean { 52 | return instance!!.getBoolean(key, def) 53 | } 54 | 55 | fun putLong(key: String, value: Long) { 56 | checkInstance() 57 | instance!!.edit().putLong(key, value).apply() 58 | } 59 | 60 | fun getLong(key: String, def: Long = 0L): Long { 61 | checkInstance() 62 | return instance!!.getLong(key, def) 63 | } 64 | 65 | fun putInt(key: String, value: Int) { 66 | checkInstance() 67 | instance!!.edit().putInt(key, value).apply() 68 | } 69 | 70 | fun getInt(key: String, def: Int = 0): Int { 71 | checkInstance() 72 | return instance!!.getInt(key, def) 73 | } 74 | 75 | fun putFloat(key: String, value: Float) { 76 | checkInstance() 77 | instance!!.edit().putFloat(key, value).apply() 78 | } 79 | 80 | fun getFloat(key: String, def: Float = 0f): Float { 81 | checkInstance() 82 | return instance!!.getFloat(key, def) 83 | } 84 | 85 | fun putStringSet(key: String, setValue: Set) { 86 | checkInstance() 87 | instance!!.edit().putStringSet(key, setValue).apply() 88 | } 89 | 90 | fun getStringSet(key: String, def: Set? = null): Set? { 91 | checkInstance() 92 | return instance!!.getStringSet(key, def) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/utils/XLog.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.utils 2 | 3 | import android.util.Log 4 | import com.ljb.mvp.kotlin.BuildConfig 5 | 6 | /** 7 | * Author:Ljb 8 | * Time:2018/12/28 9 | * There is a lot of misery in life 10 | **/ 11 | object XLog { 12 | 13 | /** 日志输出时的TAG */ 14 | private const val TAG = "XLog" 15 | 16 | /** 日志输出级别All */ 17 | const val LEVEL_ALL = 10 18 | 19 | /** 日志输出级别NONE */ 20 | const val LEVEL_NONE = 0 21 | 22 | /** 日志输出级别V */ 23 | const val LEVEL_VERBOSE = 1 24 | 25 | /** 日志输出级别D */ 26 | const val LEVEL_DEBUG = 2 27 | 28 | /** 日志输出级别I */ 29 | const val LEVEL_INFO = 3 30 | 31 | /** 日志输出级别W */ 32 | const val LEVEL_WARN = 4 33 | 34 | /** 日志输出级别E */ 35 | const val LEVEL_ERROR = 5 36 | 37 | private val DEBUG_LEVEL = if (BuildConfig.DEBUG) LEVEL_ALL else LEVEL_NONE 38 | 39 | 40 | /** 以级别为v 的形式输出LOG */ 41 | fun v(msg: String?) { 42 | if (DEBUG_LEVEL >= LEVEL_VERBOSE) { 43 | Log.v(TAG, msg) 44 | } 45 | } 46 | 47 | /** 以级别为 d 的形式输出LOG */ 48 | fun d(msg: String?) { 49 | if (DEBUG_LEVEL >= LEVEL_DEBUG) { 50 | Log.d(TAG, msg) 51 | } 52 | } 53 | 54 | /** 以级别为 i 的形式输出LOG */ 55 | fun i(msg: String?) { 56 | if (DEBUG_LEVEL >= LEVEL_INFO) { 57 | Log.i(TAG, msg) 58 | } 59 | } 60 | 61 | /** 以级别为 w 的形式输出LOG */ 62 | fun w(msg: String?) { 63 | if (DEBUG_LEVEL >= LEVEL_WARN) { 64 | Log.w(TAG, msg) 65 | } 66 | } 67 | 68 | /** 以级别为 w 的形式输出Throwable */ 69 | fun w(tr: Throwable?) { 70 | if (DEBUG_LEVEL >= LEVEL_WARN) { 71 | Log.w(TAG, "", tr) 72 | } 73 | 74 | } 75 | 76 | /** 以级别为 w 的形式输出LOG信息和Throwable */ 77 | fun w(msg: String?, tr: Throwable?) { 78 | if (DEBUG_LEVEL >= LEVEL_WARN && null != msg) { 79 | Log.w(TAG, msg, tr) 80 | } 81 | } 82 | 83 | /** 以级别为 e 的形式输出LOG */ 84 | fun e(msg: String?) { 85 | if (DEBUG_LEVEL >= LEVEL_ERROR) { 86 | Log.e(TAG, msg) 87 | } 88 | } 89 | 90 | /** 以级别为 e 的形式输出Throwable */ 91 | fun e(tr: Throwable?) { 92 | if (DEBUG_LEVEL >= LEVEL_ERROR) { 93 | Log.e(TAG, "", tr) 94 | } 95 | } 96 | 97 | /** 以级别为 e 的形式输出LOG信息和Throwable */ 98 | fun e(msg: String?, tr: Throwable) { 99 | if (DEBUG_LEVEL >= LEVEL_ERROR) { 100 | Log.e(TAG, msg, tr) 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/act/HomeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.act 2 | 3 | import android.os.Bundle 4 | import android.widget.Toast 5 | import androidx.fragment.app.Fragment 6 | import com.ljb.mvp.kotlin.R 7 | import com.ljb.mvp.kotlin.adapter.MainTabAdapter 8 | import com.ljb.mvp.kotlin.contract.HomeContract 9 | import com.ljb.mvp.kotlin.domain.TabBean 10 | import com.ljb.mvp.kotlin.view.fragment.FollowingFragment 11 | import com.ljb.mvp.kotlin.view.fragment.MyFragment 12 | import com.ljb.mvp.kotlin.view.fragment.RepositoriesFragment 13 | import com.ljb.mvp.kotlin.presenter.HomePresenter 14 | import kotlinx.android.synthetic.main.activity_home.* 15 | import mvp.ljb.kt.act.BaseMvpFragmentActivity 16 | 17 | /** 18 | * @Author:Kotlin MVP Plugin 19 | * @Date:2019/04/20 20 | * @Description input description 21 | **/ 22 | class HomeActivity : BaseMvpFragmentActivity(), HomeContract.IView { 23 | 24 | companion object { 25 | private const val KEY_INDEX = "index" 26 | } 27 | 28 | private var mFirstDownBack: Long = 0L 29 | private var mCurIndex: Int = 0 30 | 31 | private val mFragments = listOf( 32 | RepositoriesFragment(), 33 | FollowingFragment(), 34 | MyFragment()) 35 | 36 | private val mTabList = listOf( 37 | TabBean(R.drawable.bottom_tab_repos, R.string.repos), 38 | TabBean(R.drawable.bottom_tab_following, R.string.following), 39 | TabBean(R.drawable.bottom_tab_my, R.string.my)) 40 | 41 | override fun registerPresenter() = HomePresenter::class.java 42 | 43 | override fun getLayoutId() = R.layout.activity_home 44 | 45 | override fun onSaveInstanceState(outState: Bundle) { 46 | super.onSaveInstanceState(outState) 47 | outState.putInt(KEY_INDEX, mCurIndex) 48 | } 49 | 50 | override fun init(savedInstanceState: Bundle?) { 51 | super.init(savedInstanceState) 52 | savedInstanceState?.let { 53 | supportFragmentManager.popBackStackImmediate(null, 1) 54 | mCurIndex = it.getInt(KEY_INDEX) 55 | } 56 | } 57 | 58 | override fun initView() { 59 | tgv_group.setOnItemClickListener { openTabFragment(it) } 60 | tgv_group.setAdapter(MainTabAdapter(this, mTabList)) 61 | openTabFragment(mCurIndex) 62 | } 63 | 64 | 65 | private fun openTabFragment(position: Int) { 66 | tgv_group.setSelectedPosition(position) 67 | val ft = supportFragmentManager.beginTransaction() 68 | ft.hide(mFragments[mCurIndex]) 69 | var f = supportFragmentManager.findFragmentByTag(mFragments[position].javaClass.simpleName) 70 | if (f == null) { 71 | f = mFragments[position] 72 | ft.add(R.id.fl_content, f, f.javaClass.simpleName) 73 | } 74 | ft.show(f).commit() 75 | mCurIndex = position 76 | } 77 | 78 | override fun onBackPressed() { 79 | if (System.currentTimeMillis() - mFirstDownBack > 2000) { 80 | Toast.makeText(this, R.string.exit_go_out, Toast.LENGTH_SHORT).show() 81 | mFirstDownBack = System.currentTimeMillis() 82 | return 83 | } 84 | super.onBackPressed() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/act/LoginActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.act 2 | 3 | import android.Manifest 4 | import android.animation.ObjectAnimator 5 | import android.animation.PropertyValuesHolder 6 | import android.content.Intent 7 | import android.os.Bundle 8 | import android.text.TextUtils 9 | import android.view.View 10 | import com.ljb.mvp.kotlin.R 11 | import com.ljb.mvp.kotlin.common.Constant 12 | import com.ljb.mvp.kotlin.contract.LoginContract 13 | import com.ljb.mvp.kotlin.presenter.LoginPresenter 14 | import com.ljb.mvp.kotlin.utils.PermissionUtils 15 | import com.ljb.mvp.kotlin.widget.dialog.LoadingDialog 16 | import kotlinx.android.synthetic.main.activity_login.* 17 | import mvp.ljb.kt.act.BaseMvpActivity 18 | 19 | /** 20 | * @Author:Kotlin MVP Plugin 21 | * @Date:2019/04/20 22 | * @Description input description 23 | **/ 24 | class LoginActivity : BaseMvpActivity(), LoginContract.IView { 25 | 26 | private val mLoadingDialog by lazy { LoadingDialog(this) } 27 | 28 | override fun getLayoutId() = R.layout.activity_login 29 | 30 | override fun registerPresenter() = LoginPresenter::class.java 31 | 32 | override fun init(savedInstanceState: Bundle?) { 33 | initPermission() 34 | } 35 | 36 | private fun initPermission() { 37 | PermissionUtils.requestPermission(this, 38 | arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, 39 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 40 | Manifest.permission.READ_PHONE_STATE), 41 | Constant.PermissionCode.CODE_INIT) 42 | { _, _ -> 43 | //在这里处理权限结果 44 | } 45 | } 46 | 47 | override fun initView() { 48 | btn_login.setOnClickListener { login() } 49 | } 50 | 51 | override fun initData() { 52 | val login = getPresenter().getLocLogin() 53 | if (TextUtils.isEmpty(login)) { 54 | showLoginView() 55 | } else { 56 | getPresenter().delayGoHomeTask() 57 | } 58 | } 59 | 60 | override fun loginSuccess() { 61 | goHome() 62 | } 63 | 64 | override fun loginError(errorMsg: String?) { 65 | tv_tip.visibility = View.VISIBLE 66 | if (errorMsg.isNullOrEmpty()) { 67 | tv_tip.setText(R.string.net_error) 68 | } else { 69 | tv_tip.text = errorMsg 70 | } 71 | } 72 | 73 | override fun goHome() { 74 | startActivity(Intent(this, HomeActivity::class.java)) 75 | finish() 76 | } 77 | 78 | private fun showLoginView() { 79 | val alpha = PropertyValuesHolder.ofFloat("alpha", 0f, 1f) 80 | val scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1f) 81 | val scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1f) 82 | ObjectAnimator.ofPropertyValuesHolder(ll_login, alpha, scaleX, scaleY).setDuration(1000).start() 83 | ll_login.visibility = View.VISIBLE 84 | } 85 | 86 | 87 | private fun login() { 88 | if (et_github.text.isNullOrBlank()) { 89 | tv_tip.visibility = View.VISIBLE 90 | tv_tip.setText(R.string.input_user) 91 | return 92 | } 93 | getPresenter().login(et_github.text.trim().toString()) 94 | } 95 | 96 | override fun dismissLoadDialog() { 97 | mLoadingDialog.dismiss() 98 | } 99 | 100 | override fun showLoadDialog() { 101 | mLoadingDialog.show() 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/fragment/EventsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.fragment 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.ljb.mvp.kotlin.R 7 | import com.ljb.mvp.kotlin.view.act.WebViewActivity 8 | import com.ljb.mvp.kotlin.adapter.rv.EventAdapter 9 | import com.ljb.mvp.kotlin.contract.EventsContract 10 | import com.ljb.mvp.kotlin.domain.Event 11 | import com.ljb.mvp.kotlin.domain.Repository 12 | import com.ljb.mvp.kotlin.presenter.EventsPresenter 13 | import com.ljb.mvp.kotlin.widget.dialog.LoadingDialog 14 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 15 | import com.ljb.page.PageState 16 | import kotlinx.android.synthetic.main.fragment_events.* 17 | import kotlinx.android.synthetic.main.layout_recycler_view.* 18 | import mvp.ljb.kt.fragment.BaseMvpFragment 19 | 20 | /** 21 | * Created by L on 2017/7/19. 22 | */ 23 | class EventsFragment : BaseMvpFragment(), EventsContract.IView, 24 | LoadMoreRecyclerAdapter.LoadMoreListener, LoadMoreRecyclerAdapter.OnItemClickListener { 25 | 26 | private val mAdapter by lazy { EventAdapter(activity!!, mutableListOf()) } 27 | private val mLoadingDialog by lazy { LoadingDialog(activity!!) } 28 | 29 | override fun getLayoutId() = R.layout.fragment_events 30 | 31 | override fun registerPresenter() = EventsPresenter::class.java 32 | 33 | override fun initView() { 34 | page_layout.setOnPageErrorClickListener { onReload() } 35 | 36 | recycler_view.apply { 37 | layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) 38 | adapter = mAdapter 39 | mAdapter.setOnLoadMoreListener(this@EventsFragment) 40 | mAdapter.setOnItemClickListener(this@EventsFragment) 41 | } 42 | } 43 | 44 | override fun initData() { 45 | getPresenter().onRefresh() 46 | } 47 | 48 | override fun onLoadMore() { 49 | getPresenter().onLoadMore() 50 | } 51 | 52 | private fun onReload() { 53 | page_layout.setPage(PageState.STATE_LOADING) 54 | getPresenter().onRefresh() 55 | } 56 | 57 | override fun showPage(data: MutableList, page: Int) { 58 | if (page == 1) { 59 | if (data.isEmpty()) { 60 | page_layout.setPage(PageState.STATE_EMPTY) 61 | } else { 62 | page_layout.setPage(PageState.STATE_SUCCESS) 63 | mAdapter.mData.clear() 64 | mAdapter.mData.addAll(data) 65 | mAdapter.onLoadStatus(data) 66 | } 67 | } else { 68 | mAdapter.mData.addAll(data) 69 | mAdapter.onLoadStatus(data) 70 | } 71 | } 72 | 73 | override fun errorPage(t: Throwable, page: Int) { 74 | if (page == 1) { 75 | page_layout.setPage(PageState.STATE_ERROR) 76 | } else { 77 | mAdapter.onErrorStatus() 78 | } 79 | } 80 | 81 | override fun onItemClick(view: View, position: Int) { 82 | val itemData = mAdapter.mData[position] 83 | mLoadingDialog.show() 84 | getPresenter().getReposFromUrl(itemData.repo.url) 85 | } 86 | 87 | override fun setRepos(repos: Repository?) { 88 | mLoadingDialog.dismiss() 89 | repos?.let { 90 | WebViewActivity.startActivity(activity!!, it.html_url) 91 | } 92 | } 93 | 94 | 95 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/fragment/FollowersFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.fragment 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.GridLayoutManager 5 | import com.ljb.mvp.kotlin.R 6 | import com.ljb.mvp.kotlin.view.act.WebViewActivity 7 | import com.ljb.mvp.kotlin.adapter.rv.FollowersAdapter 8 | import com.ljb.mvp.kotlin.adapter.rv.FollowersDecoration 9 | import com.ljb.mvp.kotlin.contract.FollowersContract 10 | import com.ljb.mvp.kotlin.domain.Follower 11 | import com.ljb.mvp.kotlin.presenter.FollowersPresenter 12 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 13 | import com.ljb.page.PageState 14 | import kotlinx.android.synthetic.main.fragment_followers.* 15 | import kotlinx.android.synthetic.main.layout_recycler_view.* 16 | import mvp.ljb.kt.fragment.BaseMvpFragment 17 | 18 | /** 19 | * Created by L on 2017/7/19. 20 | */ 21 | class FollowersFragment : BaseMvpFragment(), FollowersContract.IView, 22 | LoadMoreRecyclerAdapter.LoadMoreListener, LoadMoreRecyclerAdapter.OnItemClickListener { 23 | 24 | private val mAdapter by lazy { FollowersAdapter(activity!!, mutableListOf()) } 25 | 26 | override fun getLayoutId() = R.layout.fragment_followers 27 | 28 | override fun registerPresenter() = FollowersPresenter::class.java 29 | 30 | override fun initView() { 31 | page_layout.setOnPageErrorClickListener { onReload() } 32 | recycler_view.apply { 33 | val manager = GridLayoutManager(context, 3) 34 | manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { 35 | override fun getSpanSize(position: Int): Int { 36 | val itemViewType = mAdapter.getItemViewType(position) 37 | return if (itemViewType == LoadMoreRecyclerAdapter.TYPE_LOAD_MORE) 3 else 1 38 | } 39 | } 40 | layoutManager = manager 41 | addItemDecoration(FollowersDecoration()) 42 | adapter = mAdapter 43 | mAdapter.setOnLoadMoreListener(this@FollowersFragment) 44 | mAdapter.setOnItemClickListener(this@FollowersFragment) 45 | } 46 | } 47 | 48 | override fun initData() { 49 | getPresenter().onRefresh() 50 | } 51 | 52 | override fun onLoadMore() { 53 | getPresenter().onLoadMore() 54 | } 55 | 56 | private fun onReload() { 57 | page_layout.setPage(PageState.STATE_LOADING) 58 | getPresenter().onRefresh() 59 | } 60 | 61 | override fun showPage(data: MutableList, page: Int) { 62 | if (page == 1) { 63 | if (data.isEmpty()) { 64 | page_layout.setPage(PageState.STATE_EMPTY) 65 | } else { 66 | page_layout.setPage(PageState.STATE_SUCCESS) 67 | mAdapter.mData.clear() 68 | mAdapter.mData.addAll(data) 69 | mAdapter.onLoadStatus(data) 70 | } 71 | } else { 72 | mAdapter.mData.addAll(data) 73 | mAdapter.onLoadStatus(data) 74 | } 75 | } 76 | 77 | override fun errorPage(t: Throwable, page: Int) { 78 | if (page == 1) { 79 | page_layout.setPage(PageState.STATE_ERROR) 80 | } else { 81 | mAdapter.onErrorStatus() 82 | } 83 | } 84 | 85 | override fun onItemClick(view: View, position: Int) { 86 | val itemData = mAdapter.mData[position] 87 | WebViewActivity.startActivity(activity!!, itemData.html_url) 88 | } 89 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/fragment/MyFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.fragment 2 | 3 | import android.animation.AnimatorSet 4 | import android.animation.ObjectAnimator 5 | import android.content.DialogInterface 6 | import android.content.Intent 7 | import com.ljb.mvp.kotlin.R 8 | import com.ljb.mvp.kotlin.adapter.MyTabAdapter 9 | import com.ljb.mvp.kotlin.contract.MyContract 10 | import com.ljb.mvp.kotlin.domain.MyTabFragmentBean 11 | import com.ljb.mvp.kotlin.domain.User 12 | import com.ljb.mvp.kotlin.img.ImageLoader 13 | import com.ljb.mvp.kotlin.presenter.MyPresenter 14 | import com.ljb.mvp.kotlin.view.act.LoginActivity 15 | import com.ljb.mvp.kotlin.widget.dialog.NormalMsgDialog 16 | import kotlinx.android.synthetic.main.fragment_my.* 17 | import mvp.ljb.kt.fragment.BaseMvpFragment 18 | 19 | 20 | /** 21 | * Created by L on 2017/7/18. 22 | */ 23 | class MyFragment : BaseMvpFragment(), MyContract.IView { 24 | 25 | private val mTabArr by lazy { 26 | arrayOf( 27 | MyTabFragmentBean(getString(R.string.events), EventsFragment()), 28 | MyTabFragmentBean(getString(R.string.starred), StarredFragment()), 29 | MyTabFragmentBean(getString(R.string.followers), FollowersFragment()) 30 | ) 31 | } 32 | 33 | private val mLogoutDialog by lazy { 34 | NormalMsgDialog(activity!!) 35 | .setMessage(R.string.logout_user) 36 | .setLeftButtonInfo(R.string.cancel) 37 | .setRightButtonInfo(R.string.enter, DialogInterface.OnClickListener { _, _ -> 38 | getPresenter().logout() 39 | }) 40 | } 41 | 42 | override fun getLayoutId() = R.layout.fragment_my 43 | 44 | override fun registerPresenter() = MyPresenter::class.java 45 | 46 | override fun initView() { 47 | viewpager.apply { 48 | offscreenPageLimit = mTabArr.size 49 | adapter = MyTabAdapter(childFragmentManager, mTabArr) 50 | } 51 | tab_layout.setViewPager(viewpager) 52 | btn_logout.setOnClickListener { mLogoutDialog.show() } 53 | } 54 | 55 | override fun initData() { 56 | getPresenter().getUserInfo() 57 | } 58 | 59 | override fun logoutSuccess() { 60 | goLogin() 61 | } 62 | 63 | private fun goLogin() { 64 | startActivity(Intent(activity, LoginActivity::class.java)) 65 | } 66 | 67 | override fun showUserInfo(user: User) { 68 | ImageLoader.load(activity!!, user.avatar_url, iv_header, ImageLoader.getCircleRequest()) 69 | tv_name.text = user.login 70 | tv_location.text = user.location 71 | tv_company.text = user.company 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/fragment/RepositoriesFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.fragment 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import com.ljb.mvp.kotlin.R 6 | import com.ljb.mvp.kotlin.view.act.WebViewActivity 7 | import com.ljb.mvp.kotlin.adapter.rv.RepositoriesAdapter 8 | import com.ljb.mvp.kotlin.contract.RepositoriesContract 9 | import com.ljb.mvp.kotlin.domain.Repository 10 | import com.ljb.mvp.kotlin.presenter.RepositoriesPresenter 11 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 12 | import com.ljb.page.PageState 13 | import kotlinx.android.synthetic.main.fragment_repos.* 14 | import kotlinx.android.synthetic.main.layout_refresh_recycler_view.* 15 | import mvp.ljb.kt.fragment.BaseMvpFragment 16 | 17 | /** 18 | * Repos page 19 | * Created by L on 2017/7/18. 20 | */ 21 | class RepositoriesFragment : BaseMvpFragment(), RepositoriesContract.IView, 22 | LoadMoreRecyclerAdapter.LoadMoreListener, LoadMoreRecyclerAdapter.OnItemClickListener { 23 | 24 | private val mAdapter by lazy { RepositoriesAdapter(activity!!, mutableListOf()) } 25 | 26 | override fun getLayoutId() = R.layout.fragment_repos 27 | 28 | override fun registerPresenter() = RepositoriesPresenter::class.java 29 | 30 | override fun initView() { 31 | page_layout.setOnPageErrorClickListener { onReload() } 32 | refresh_layout.apply { 33 | setColorSchemeResources(R.color.color_39B6DF) 34 | setOnRefreshListener { getPresenter().onRefresh() } 35 | } 36 | recycler_view.apply { 37 | layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 38 | adapter = mAdapter 39 | mAdapter.setOnLoadMoreListener(this@RepositoriesFragment) 40 | mAdapter.setOnItemClickListener(this@RepositoriesFragment) 41 | } 42 | } 43 | 44 | override fun initData() { 45 | getPresenter().onRefresh() 46 | } 47 | 48 | override fun onLoadMore() { 49 | getPresenter().onLoadMore() 50 | } 51 | 52 | private fun onReload() { 53 | page_layout.setPage(PageState.STATE_LOADING) 54 | getPresenter().onRefresh() 55 | } 56 | 57 | override fun showPage(data: MutableList, page: Int) { 58 | if (page == 1) { 59 | refresh_layout.isRefreshing = false 60 | if (data.isEmpty()) { 61 | page_layout.setPage(PageState.STATE_EMPTY) 62 | } else { 63 | page_layout.setPage(PageState.STATE_SUCCESS) 64 | mAdapter.mData.clear() 65 | mAdapter.mData.addAll(data) 66 | mAdapter.onLoadStatus(data) 67 | } 68 | } else { 69 | mAdapter.mData.addAll(data) 70 | mAdapter.onLoadStatus(data) 71 | } 72 | } 73 | 74 | override fun errorPage(t: Throwable, page: Int) { 75 | if (page == 1) { 76 | page_layout.setPage(PageState.STATE_ERROR) 77 | } else { 78 | mAdapter.onErrorStatus() 79 | } 80 | } 81 | 82 | override fun onItemClick(view: View, position: Int) { 83 | val itemData = mAdapter.mData[position] 84 | WebViewActivity.startActivity(activity!!, itemData.html_url) 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/view/fragment/StarredFragment.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.view.fragment 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import com.ljb.mvp.kotlin.R 6 | import com.ljb.mvp.kotlin.view.act.WebViewActivity 7 | import com.ljb.mvp.kotlin.adapter.rv.StarredAdapter 8 | import com.ljb.mvp.kotlin.contract.StarredContract 9 | import com.ljb.mvp.kotlin.domain.Starred 10 | import com.ljb.mvp.kotlin.presenter.StarredPresenter 11 | import com.ljb.mvp.kotlin.widget.loadmore.LoadMoreRecyclerAdapter 12 | import com.ljb.page.PageState 13 | import kotlinx.android.synthetic.main.fragment_starred.* 14 | import kotlinx.android.synthetic.main.layout_recycler_view.* 15 | import mvp.ljb.kt.fragment.BaseMvpFragment 16 | 17 | /** 18 | * Created by L on 2017/7/19. 19 | */ 20 | class StarredFragment : BaseMvpFragment(), StarredContract.IView, 21 | LoadMoreRecyclerAdapter.LoadMoreListener, LoadMoreRecyclerAdapter.OnItemClickListener { 22 | 23 | private val mAdapter: StarredAdapter by lazy { StarredAdapter(activity!!, mutableListOf()) } 24 | 25 | override fun getLayoutId() = R.layout.fragment_starred 26 | 27 | override fun registerPresenter() = StarredPresenter::class.java 28 | 29 | override fun initView() { 30 | page_layout.setOnPageErrorClickListener { onReload() } 31 | recycler_view.apply { 32 | layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 33 | adapter = mAdapter 34 | mAdapter.setOnLoadMoreListener(this@StarredFragment) 35 | mAdapter.setOnItemClickListener(this@StarredFragment) 36 | } 37 | } 38 | 39 | override fun initData() { 40 | getPresenter().onRefresh() 41 | } 42 | 43 | override fun onLoadMore() { 44 | getPresenter().onLoadMore() 45 | } 46 | 47 | private fun onReload() { 48 | page_layout.setPage(PageState.STATE_LOADING) 49 | getPresenter().onRefresh() 50 | } 51 | 52 | override fun showPage(data: MutableList, page: Int) { 53 | if (page == 1) { 54 | if (data.isEmpty()) { 55 | page_layout.setPage(PageState.STATE_EMPTY) 56 | } else { 57 | page_layout.setPage(PageState.STATE_SUCCESS) 58 | mAdapter.mData.clear() 59 | mAdapter.mData.addAll(data) 60 | mAdapter.onLoadStatus(data) 61 | } 62 | } else { 63 | mAdapter.mData.addAll(data) 64 | mAdapter.onLoadStatus(data) 65 | } 66 | } 67 | 68 | override fun errorPage(t: Throwable, page: Int) { 69 | if (page == 1) { 70 | page_layout.setPage(PageState.STATE_ERROR) 71 | } else { 72 | mAdapter.onErrorStatus() 73 | } 74 | } 75 | 76 | override fun onItemClick(view: View, position: Int) { 77 | val itemData = mAdapter.mData[position] 78 | WebViewActivity.startActivity(activity!!, itemData.html_url) 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/webview/JsApi.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.webview 2 | 3 | import android.content.Context 4 | import android.webkit.JavascriptInterface 5 | 6 | class JsApi(private val mContext: Context) { 7 | 8 | 9 | @JavascriptInterface 10 | fun webCallTest() { 11 | 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/webview/SimpleWebActionCallBack.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.webview 2 | 3 | import android.graphics.Bitmap 4 | import android.webkit.WebResourceError 5 | import android.webkit.WebResourceRequest 6 | import android.webkit.WebView 7 | 8 | /** 9 | * Author:Ljb 10 | * Time:2019/4/3 11 | * There is a lot of misery in life 12 | **/ 13 | open class SimpleWebActionCallBack : WebActionCallBack { 14 | 15 | override fun onReceivedTitle(view: WebView?, title: String?) { 16 | } 17 | 18 | override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) { 19 | } 20 | 21 | override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { 22 | return false 23 | } 24 | 25 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { 26 | } 27 | 28 | override fun onPageFinished(view: WebView?, url: String?) { 29 | } 30 | 31 | override fun onProgressChanged(view: WebView?, newProgress: Int) { 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/webview/WebActionCallBack.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.webview 2 | 3 | import android.graphics.Bitmap 4 | import android.webkit.WebResourceError 5 | import android.webkit.WebResourceRequest 6 | import android.webkit.WebView 7 | 8 | /** 9 | * Author:Ljb 10 | * Time:2019/4/3 11 | * There is a lot of misery in life 12 | **/ 13 | interface WebActionCallBack { 14 | 15 | /** 16 | * 页面Title回调 17 | * */ 18 | fun onReceivedTitle(view: WebView?, title: String?) 19 | 20 | /** 21 | * 页面发生错误 22 | * */ 23 | fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) 24 | 25 | /** 26 | * 是否拦截Url 27 | * */ 28 | fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean 29 | 30 | /** 31 | * 页面加载完毕 32 | * */ 33 | fun onPageFinished(view: WebView?, url: String?) 34 | 35 | /** 36 | * 页面开始加载 37 | * */ 38 | fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) 39 | 40 | /** 41 | * 页面加载进度 42 | * */ 43 | fun onProgressChanged(view: WebView?, newProgress: Int) 44 | 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/widget/TabGroupView.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.widget 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.Gravity 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.LinearLayout 9 | import com.ljb.mvp.kotlin.R 10 | 11 | 12 | /** 13 | * 底部Tab导航栏 14 | * Created by L on 2017/7/10. 15 | */ 16 | class TabGroupView : LinearLayout { 17 | 18 | private var mOnItemClickListener: ((position: Int) -> Unit)? = null 19 | 20 | constructor(context: Context) : super(context) 21 | 22 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) 23 | 24 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) 25 | 26 | fun setOnItemClickListener(listener: (position: Int) -> Unit) { 27 | mOnItemClickListener = listener 28 | } 29 | 30 | fun setAdapter(adapter: TabAdapter?) { 31 | if (adapter != null && adapter.getCount() > 0) { 32 | for (i in 0 until adapter.getCount()) { 33 | val tabView = adapter.getTabView(i, this) 34 | val params = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT) 35 | params.weight = 1f 36 | params.gravity = Gravity.CENTER 37 | addView(tabView, params) 38 | tabView.setOnClickListener { mOnItemClickListener?.invoke(i) } 39 | } 40 | } 41 | } 42 | 43 | fun setSelectedPosition(position: Int) { 44 | initUnSelected() 45 | getChildAt(position).findViewById(R.id.bottom_tab_icon)?.isSelected = true 46 | getChildAt(position).findViewById(R.id.bottom_tab_text)?.isSelected = true 47 | } 48 | 49 | private fun initUnSelected() { 50 | (0 until childCount).mapNotNull { 51 | getChildAt(it).findViewById(R.id.bottom_tab_icon)?.isSelected = false 52 | getChildAt(it).findViewById(R.id.bottom_tab_text)?.isSelected = false 53 | } 54 | } 55 | 56 | 57 | interface TabAdapter { 58 | fun getCount(): Int 59 | fun getTabView(position: Int, parent: ViewGroup?): View 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/widget/dialog/LoadingDialog.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.widget.dialog 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.view.View 6 | import android.view.Window 7 | import com.ljb.mvp.kotlin.R 8 | 9 | class LoadingDialog(context: Context, val isBack: Boolean = true) : Dialog(context) { 10 | 11 | constructor(context: Context) : this(context, true) 12 | 13 | init { 14 | requestWindowFeature(Window.FEATURE_NO_TITLE) 15 | window?.setBackgroundDrawableResource(android.R.color.transparent) 16 | window?.setDimAmount(0f) 17 | setContentView(View.inflate(getContext(), R.layout.dialog_loading, null)) 18 | setCancelable(isBack) 19 | setCanceledOnTouchOutside(isBack) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/widget/dialog/NormalMsgDialog.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.widget.dialog 2 | 3 | import android.app.Activity 4 | import android.app.Dialog 5 | import android.content.DialogInterface 6 | import android.text.TextUtils 7 | import android.view.Gravity 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.view.Window 11 | import android.widget.Button 12 | import android.widget.TextView 13 | import com.ljb.mvp.kotlin.R 14 | 15 | /** 16 | * Created by L on 2017/12/29. 17 | */ 18 | class NormalMsgDialog(private val mActivity: Activity) : View.OnClickListener { 19 | 20 | private val mDialog: Dialog = Dialog(mActivity, R.style.mask_dialog) 21 | private val mView: View = View.inflate(mActivity, R.layout.dialog_msg_normal, null) 22 | private val mTvMsg = mView.findViewById(R.id.tv_msg) as TextView 23 | private val mBtnLeft = mView.findViewById(R.id.btn_left) as Button 24 | private val mBtnRight = mView.findViewById(R.id.btn_right) as Button 25 | 26 | private var mRightButtonClickListener: DialogInterface.OnClickListener? = null 27 | private var mLeftButtonClickListener: DialogInterface.OnClickListener? = null 28 | 29 | init { 30 | mDialog.setContentView(mView, ViewGroup.LayoutParams(dip2px(270f), ViewGroup.LayoutParams.MATCH_PARENT)) 31 | mDialog.setFeatureDrawableAlpha(Window.FEATURE_OPTIONS_PANEL, 0) 32 | mDialog.window.setGravity(Gravity.CENTER) 33 | 34 | mBtnLeft.setOnClickListener(this) 35 | mBtnRight.setOnClickListener(this) 36 | } 37 | 38 | private fun dip2px(dip: Float): Int { 39 | val scale = mActivity.resources.displayMetrics.density 40 | return (dip * scale + 0.5f).toInt() 41 | } 42 | 43 | override fun onClick(v: View) { 44 | if (v.id == R.id.btn_left) { 45 | mLeftButtonClickListener?.onClick(mDialog, DialogInterface.BUTTON_NEGATIVE) 46 | } else if (v.id == R.id.btn_right) { 47 | mRightButtonClickListener?.onClick(mDialog, DialogInterface.BUTTON_POSITIVE) 48 | } 49 | dismiss() 50 | } 51 | 52 | fun show() { 53 | mDialog.show() 54 | } 55 | 56 | fun dismiss() { 57 | if (mDialog.isShowing) { 58 | mDialog.dismiss() 59 | } 60 | } 61 | 62 | 63 | fun setMessage(strId: String): NormalMsgDialog { 64 | mTvMsg.text = strId 65 | return this 66 | } 67 | 68 | fun setMessage(strId: Int): NormalMsgDialog { 69 | mTvMsg.setText(strId) 70 | return this 71 | } 72 | 73 | 74 | fun setLeftButtonInfo(strId: Int, listener: DialogInterface.OnClickListener? = null): NormalMsgDialog { 75 | return setLeftButtonInfo(mActivity.getString(strId), listener) 76 | } 77 | 78 | fun setRightButtonInfo(strId: Int, listener: DialogInterface.OnClickListener? = null): NormalMsgDialog { 79 | return setRightButtonInfo(mActivity.getString(strId), listener) 80 | } 81 | 82 | fun setLeftButtonInfo(str: String, listener: DialogInterface.OnClickListener? = null): NormalMsgDialog { 83 | if (!TextUtils.isEmpty(str)) { 84 | mBtnLeft.text = str 85 | } 86 | this.mLeftButtonClickListener = listener 87 | return this 88 | } 89 | 90 | fun setRightButtonInfo(str: String, listener: DialogInterface.OnClickListener? = null): NormalMsgDialog { 91 | if (!TextUtils.isEmpty(str)) { 92 | mBtnRight.text = str 93 | } 94 | this.mRightButtonClickListener = listener 95 | return this 96 | } 97 | 98 | 99 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ljb/mvp/kotlin/widget/loadmore/LoadMoreHolder.kt: -------------------------------------------------------------------------------- 1 | package com.ljb.mvp.kotlin.widget.loadmore 2 | 3 | import android.view.View 4 | import android.widget.RelativeLayout 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.ljb.mvp.kotlin.R 7 | 8 | /** 9 | * Created by L on 2017/1/16. 10 | */ 11 | class LoadMoreHolder(view: View, private val mAdapter: LoadMoreRecyclerAdapter<*>) : RecyclerView.ViewHolder(view) { 12 | 13 | private var mCurType: LoadMoreType = LoadMoreType.LoadMore 14 | private var rl_more_loading: RelativeLayout? = null 15 | private var rl_more_error: RelativeLayout? = null 16 | private var rl_more_not: RelativeLayout? = null 17 | 18 | enum class LoadMoreType { 19 | LoadMore, Error, NoMore 20 | } 21 | 22 | init { 23 | initView(view) 24 | initStatus() 25 | } 26 | 27 | private fun initView(view: View) { 28 | rl_more_error = view.findViewById(R.id.rl_more_error) as RelativeLayout 29 | rl_more_loading = view.findViewById(R.id.rl_more_loading) as RelativeLayout 30 | rl_more_not = view.findViewById(R.id.rl_more_not) as RelativeLayout 31 | rl_more_error!!.setOnClickListener { reLoadMore() } 32 | } 33 | 34 | private fun initStatus() { 35 | rl_more_loading!!.visibility = if (mCurType == LoadMoreType.LoadMore) View.VISIBLE else View.GONE 36 | rl_more_error!!.visibility = if (mCurType == LoadMoreType.Error) View.VISIBLE else View.GONE 37 | rl_more_not!!.visibility = if (mCurType == LoadMoreType.NoMore) View.VISIBLE else View.GONE 38 | } 39 | 40 | private fun reLoadMore() { 41 | if (mCurType == LoadMoreType.Error) { 42 | mCurType = LoadMoreType.LoadMore 43 | initStatus() 44 | mAdapter.loadMore() 45 | } 46 | } 47 | 48 | fun getType(): LoadMoreType = mCurType 49 | 50 | fun setStatus(mCurType: LoadMoreType) { 51 | this.mCurType = mCurType 52 | initStatus() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/anim/share_dialog_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/share_dialog_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/icon_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-hdpi/icon_app.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/icon_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-mdpi/icon_app.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_app.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_company.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_company.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_logo_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_logo_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_page_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_page_empty.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_page_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xhdpi/icon_page_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bg_my_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bg_my_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bottom_tab_box_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bottom_tab_box_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bottom_tab_box_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bottom_tab_box_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bottom_tab_following_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bottom_tab_following_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bottom_tab_following_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bottom_tab_following_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bottom_tab_my_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bottom_tab_my_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/bottom_tab_my_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/bottom_tab_my_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/default_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/default_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/icon_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/icon_app.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/icon_user_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxhdpi/icon_user_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/icon_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxxhdpi/icon_app.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/test_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/app/src/main/res/drawable-xxxhdpi/test_header.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_tab_following.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_tab_my.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_tab_repos.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_tab_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_loading_dialog_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_reload_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_round_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_round_white_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 24 | 25 | 35 | 36 | 46 | 47 | 55 | 56 |