├── .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 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 | 
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 | 
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 | 
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 | 
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 | 
98 |
99 | ### 插件演示
100 |
101 | * 自动生成View、Presenter、Model、Contract Kotlin文件
102 |
103 | > 操作:包目录右键 -> New MVP Kotlin -> 输入模块名称 -> OK
104 |
105 | 
106 |
107 |
108 | * 自动生成View、Presenter、Model、Contract Java文件
109 |
110 | > 操作:包目录右键 -> New MVP Java -> 输入模块名称 -> OK
111 |
112 | ## Demo App 截图
113 |
114 | 
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 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_web.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_msg_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
23 |
24 |
25 |
29 |
30 |
34 |
35 |
46 |
47 |
51 |
52 |
63 |
64 |
65 |
66 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_events.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_followers.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_following.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_repos.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
22 |
23 |
28 |
29 |
30 |
31 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_starred.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_event.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
22 |
23 |
24 |
34 |
35 |
36 |
44 |
45 |
53 |
54 |
55 |
59 |
60 |
67 |
68 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_followers.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_following.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_bottom_tab_defalut.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
19 |
20 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_common_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
31 |
32 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_load_more.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
19 |
20 |
21 |
22 |
27 |
28 |
37 |
38 |
39 |
40 |
45 |
46 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_page_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_page_error.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
19 |
20 |
25 |
26 |
34 |
35 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_page_load.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_recycler_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_refresh_recycler_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000
4 | #000
5 | #d3d3d3
6 |
7 | #F0EFF5
8 | #333
9 | #39B6DF
10 | #666
11 | #999
12 | #FF1A1A
13 | #d2d2d2
14 | #A6B6BB
15 | #F18E33
16 | #239A3B
17 | #fff
18 | #9000
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MVP-Kotlin
3 | github username
4 | Login
5 | Input github username
6 | Net Error
7 | Repos
8 | My
9 | Following
10 | Logout
11 | Events
12 | Starred
13 | Followers
14 | Network Error
15 | reload
16 | loading…
17 | load failed , click retry
18 | no more data
19 | double click to exit
20 | action:\t%s
21 | Cancel
22 | Enter
23 | Exit the current user?
24 | start: %s
25 | fork: %s
26 | issues: %s
27 | update: %s
28 | create: %s
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
24 |
25 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ljb/mvp/kotlin/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ljb.mvp.kotlin;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.31'
5 | repositories {
6 | jcenter()
7 | google()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.4.2'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | jcenter()
21 | google()
22 | maven { url 'https://jitpack.io' }
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/daolib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/daolib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 28
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 28
10 | versionCode 101
11 | versionName "1.0.1"
12 |
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: 'libs', include: ['*.jar'])
26 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
27 | //rxjava
28 | api 'io.reactivex.rxjava2:rxandroid:2.0.2'
29 | api 'io.reactivex.rxjava2:rxjava:2.2.0'
30 | }
31 | repositories {
32 | mavenCentral()
33 | }
34 |
--------------------------------------------------------------------------------
/daolib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/daolib/src/androidTest/java/dao/ljb/kt/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt;
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 | * Instrumented 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() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("dao.ljb.kt.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/daolib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/DaoConfig.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt
2 |
3 | import android.annotation.SuppressLint
4 | import dao.ljb.kt.core.DatabaseHelper
5 | import dao.ljb.kt.core.IDaoProtocolConfig
6 |
7 | /**
8 | * Author:Ljb
9 | * Time:2018/12/7
10 | * There is a lot of misery in life
11 | **/
12 | object DaoConfig {
13 |
14 | @SuppressLint("StaticFieldLeak")
15 | private var mDbHelper: DatabaseHelper? = null
16 | private var mTransform: IDaoProtocolConfig? = null
17 |
18 | fun init(helper: DatabaseHelper, protocolTransform: IDaoProtocolConfig) {
19 | mDbHelper = helper
20 | mTransform = protocolTransform
21 | }
22 |
23 | fun getHelper(): DatabaseHelper {
24 | if (mDbHelper == null) throw IllegalStateException("dao not init")
25 | return mDbHelper!!
26 | }
27 |
28 | fun getTransform(): IDaoProtocolConfig {
29 | if (mTransform == null) throw IllegalStateException("dao not init")
30 | return mTransform!!
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/core/BaseDaoProtocol.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.core
2 |
3 | import dao.ljb.kt.db.DatabaseOpenHelper
4 | import io.reactivex.Observable
5 |
6 | /**
7 | * Author:Ljb
8 | * Time:2018/11/9
9 | * There is a lot of misery in life
10 | **/
11 | abstract class BaseDaoProtocol {
12 |
13 | protected val mSqliteDb by lazy { DatabaseOpenHelper.getDefInstance().writableDatabase }
14 |
15 | fun createObservable(f: () -> T): Observable {
16 | return Observable.create {
17 | try {
18 | val result: T = f()
19 | it.onNext(result)
20 | it.onComplete()
21 | } catch (e: Exception) {
22 | it.onError(e)
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/core/DaoFactory.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.core
2 |
3 | import dao.ljb.kt.DaoConfig
4 |
5 | /**
6 | * Author:Ljb
7 | * Time:2018/11/9
8 | * There is a lot of misery in life
9 | **/
10 | object DaoFactory {
11 |
12 | private val mDaoGroup = DaoProtocolGroup()
13 |
14 | private fun getNewProtocol(clazz: Class): T = DaoConfig.getTransform().transformProtocol(clazz)
15 |
16 | @Suppress("UNCHECKED_CAST")
17 | fun getProtocol(clazz: Class): T {
18 | return mDaoGroup.getProtocol(clazz) ?: registerNewProtocol(clazz)
19 | }
20 |
21 | private fun registerNewProtocol(clazz: Class): T {
22 | val protocol = getNewProtocol(clazz)
23 | mDaoGroup.register(clazz, protocol)
24 | return protocol
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/core/DaoProtocolGroup.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.core
2 |
3 | import java.util.*
4 |
5 | /**
6 | * Author:Ljb
7 | * Time:2018/11/9
8 | * There is a lot of misery in life
9 | **/
10 | class DaoProtocolGroup {
11 |
12 | private val map = WeakHashMap, IDaoInterface>()
13 |
14 | fun register(key: Class, value: IDaoInterface) {
15 | if (key.isAssignableFrom(value::class.java)) map[key] = value
16 | else throw IllegalStateException("Dao interface register error : value implements key ?")
17 | }
18 |
19 | @Suppress("UNCHECKED_CAST")
20 | fun getProtocol(key: Class): T? {
21 | val protocol = map[key]
22 | return if (protocol == null) null
23 | else protocol as T
24 | }
25 | }
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/core/DatabaseHelper.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.core
2 |
3 | import android.content.Context
4 | import android.database.sqlite.SQLiteDatabase
5 |
6 | /**
7 | * Author:Ljb
8 | * Time:2018/12/7
9 | * There is a lot of misery in life
10 | **/
11 | abstract class DatabaseHelper(val context: Context, val databaseName: String, val version: Int) {
12 |
13 | abstract fun onCreate(db: SQLiteDatabase)
14 |
15 | abstract fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int)
16 | }
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/core/IDaoInterface.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.core
2 |
3 | /**
4 | * Author:Ljb
5 | * Time:2018/11/9
6 | * There is a lot of misery in life
7 | **/
8 | interface IDaoInterface
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/core/IDaoProtocolConfig.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.core
2 |
3 | /**
4 | * Author:Ljb
5 | * Time:2018/12/7
6 | * There is a lot of misery in life
7 | **/
8 | interface IDaoProtocolConfig {
9 |
10 | fun transformProtocol(clazz: Class): T
11 | }
12 |
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/db/DatabaseOpenHelper.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.db
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.database.sqlite.SQLiteDatabase
6 | import android.database.sqlite.SQLiteOpenHelper
7 | import dao.ljb.kt.DaoConfig
8 |
9 | /**
10 | * Author:Ljb
11 | * Time:2018/11/8
12 | * There is a lot of misery in life
13 | **/
14 |
15 | class DatabaseOpenHelper(context: Context) : SQLiteOpenHelper(context, DaoConfig.getHelper().databaseName, null, DaoConfig.getHelper().version) {
16 |
17 | //单例
18 | companion object {
19 |
20 | @SuppressLint("StaticFieldLeak")
21 | private var instance: DatabaseOpenHelper? = null
22 |
23 | fun getDefInstance(): DatabaseOpenHelper {
24 | if (instance != null) return instance as DatabaseOpenHelper
25 | return if (instance != null) {
26 | instance as DatabaseOpenHelper
27 | } else {
28 | instance = DatabaseOpenHelper(DaoConfig.getHelper().context)
29 | instance as DatabaseOpenHelper
30 | }
31 | }
32 | }
33 |
34 | override fun onCreate(db: SQLiteDatabase) {
35 | DaoConfig.getHelper().onCreate(db)
36 | }
37 |
38 | override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
39 | DaoConfig.getHelper().onUpgrade(db, oldVersion, newVersion)
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/daolib/src/main/java/dao/ljb/kt/table/BaseTable.kt:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt.table
2 |
3 | import android.text.TextUtils
4 |
5 | /**
6 | * Author:Ljb
7 | * Time:2018/11/8
8 | * There is a lot of misery in life
9 | **/
10 |
11 | abstract class BaseTable {
12 |
13 | companion object {
14 | const val TYPE_TEXT = "TEXT"
15 | const val TYPE_LONG = "LONG"
16 | const val TYPE_INTEGER = "INTEGER"
17 | }
18 |
19 | private var mTableName = ""
20 | private var mColumnsMap: Map? = null
21 |
22 | protected abstract fun createTableName(): String
23 |
24 | protected abstract fun createColumns(): Map
25 |
26 | fun getName(): String {
27 | if (TextUtils.isEmpty(mTableName)) {
28 | val tempName = createTableName()
29 | if (TextUtils.isEmpty(tempName)) throw IllegalStateException("table name is empty or null")
30 | mTableName = tempName
31 | }
32 | return mTableName
33 | }
34 |
35 | fun getColumns(): Map {
36 | if (mColumnsMap == null || mColumnsMap!!.isEmpty()) {
37 | val tempMap = createColumns()
38 | if (tempMap.isEmpty()) throw IllegalStateException("table columns is empty or null")
39 | mColumnsMap = tempMap
40 | }
41 | return mColumnsMap!!
42 | }
43 |
44 |
45 | }
--------------------------------------------------------------------------------
/daolib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | daolib
3 |
4 |
--------------------------------------------------------------------------------
/daolib/src/test/java/dao/ljb/kt/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package dao.ljb.kt;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | android.useAndroidX=true
19 | android.enableJetifier=true
20 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 27 15:55:32 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/img/anim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/img/anim.gif
--------------------------------------------------------------------------------
/img/model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/img/model.png
--------------------------------------------------------------------------------
/img/mvp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/img/mvp.png
--------------------------------------------------------------------------------
/img/mvp_plugin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/img/mvp_plugin.gif
--------------------------------------------------------------------------------
/img/plugin_install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/img/plugin_install.png
--------------------------------------------------------------------------------
/img/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cn-ljb/mvp-kotlin/43efb3d01296613c0b823e72c53548f98d41452a/img/qrcode.png
--------------------------------------------------------------------------------
/netlib/.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 |
--------------------------------------------------------------------------------
/netlib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 28
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 28
10 | versionCode 101
11 | versionName "1.0.1"
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
25 |
26 | //okhttp
27 | implementation 'com.squareup.okhttp3:okhttp:3.11.0'
28 | //retrofit
29 | implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1'
30 | api 'com.squareup.retrofit2:retrofit:2.4.0'
31 | implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
32 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
33 | //rxjava
34 | api 'io.reactivex.rxjava2:rxandroid:2.0.2'
35 | api 'io.reactivex.rxjava2:rxjava:2.2.0'
36 | }
37 | repositories {
38 | mavenCentral()
39 | }
40 |
--------------------------------------------------------------------------------
/netlib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/netlib/src/androidTest/java/com/senyint/netlib/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.senyint.netlib;
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 | * Instrumented 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() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.senyint.netlib.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/netlib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/netlib/src/main/java/net/ljb/kt/HttpConfig.kt:
--------------------------------------------------------------------------------
1 | package net.ljb.kt
2 |
3 | import net.ljb.kt.utils.NetLog
4 |
5 | object HttpConfig {
6 | private var mHeaderMap: Map? = null
7 | private var mParamMap: Map? = null
8 | private var mBaseUrl: String = ""
9 | private var isLog: Boolean = true
10 |
11 | fun init(baseUrl: String, headers: Map? = null, params: Map? = null, isLog: Boolean = false) {
12 | setBaseUrl(baseUrl)
13 | headers?.apply { setHeader(this) }
14 | params?.apply { setParam(this) }
15 | setLogEnable(isLog)
16 | }
17 |
18 | fun setBaseUrl(url: String) {
19 | mBaseUrl = url
20 | }
21 |
22 | fun getBaseUrl(): String {
23 | return checkNotNull(mBaseUrl)
24 | }
25 |
26 | fun setHeader(map: Map) {
27 | mHeaderMap = map
28 | }
29 |
30 | fun setParam(map: Map) {
31 | mParamMap = map
32 | }
33 |
34 | fun getHeader(): Map? {
35 | return mHeaderMap
36 | }
37 |
38 | fun getParam(): Map? {
39 | return mParamMap
40 | }
41 |
42 | fun setLogEnable(isLog: Boolean) {
43 | this.isLog = isLog
44 | }
45 |
46 | fun getLogLv(): Int {
47 | return if (isLog) NetLog.LEVEL_ALL else NetLog.LEVEL_NONE
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/netlib/src/main/java/net/ljb/kt/client/HttpFactory.kt:
--------------------------------------------------------------------------------
1 | package net.ljb.kt.client
2 |
3 | import android.util.Log
4 | import java.util.*
5 |
6 | /**
7 | * WBHttpClient
8 | * 1、可使用 OkHttp
9 | * 2、也可使用 Retrofit
10 | * Created by L on 2017/6/8.
11 | */
12 | object HttpFactory {
13 |
14 | private val mProtocolMap = WeakHashMap, Any>()
15 | private val mStrProtocolMap = WeakHashMap, Any>()
16 |
17 | @Suppress("UNCHECKED_CAST")
18 | fun getProtocol(clazz: Class, isStrClient: Boolean = false): T {
19 | val map = if (isStrClient) mStrProtocolMap else mProtocolMap
20 | return if (map.contains(clazz)) {
21 | map[clazz] as T
22 | } else {
23 | val protocol = if (isStrClient) {
24 | HttpClient.getStrRetrofit().create(clazz)
25 | } else {
26 | HttpClient.getRetrofit().create(clazz)
27 | }
28 | map[clazz] = protocol
29 | protocol
30 | }
31 | }
32 |
33 | @Suppress("UNCHECKED_CAST")
34 | fun getProtocol(clazz: Class): T {
35 | Log.i("XLog", "protocol size : ${mProtocolMap.size}")
36 | return if (mProtocolMap.contains(clazz)) {
37 | mProtocolMap[clazz] as T
38 | } else {
39 | val protocol = HttpClient.getRetrofit().create(clazz)
40 | mProtocolMap[clazz] = protocol
41 | protocol
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/netlib/src/main/java/net/ljb/kt/client/HttpMethod.kt:
--------------------------------------------------------------------------------
1 | package net.ljb.kt.client
2 |
3 | enum class HttpMethod(val method: String) {
4 | GET("GET"),
5 | POST("POST"),
6 | PUT("PUT"),
7 | DELETE("DELETE")
8 | }
9 |
--------------------------------------------------------------------------------
/netlib/src/main/java/net/ljb/kt/client/StringConverterFactory.java:
--------------------------------------------------------------------------------
1 | package net.ljb.kt.client;
2 |
3 | import java.io.IOException;
4 | import java.lang.annotation.Annotation;
5 | import java.lang.reflect.Type;
6 |
7 | import okhttp3.MediaType;
8 | import okhttp3.RequestBody;
9 | import okhttp3.ResponseBody;
10 | import retrofit2.Converter;
11 | import retrofit2.Retrofit;
12 |
13 | public class StringConverterFactory extends Converter.Factory {
14 |
15 | private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
16 |
17 | @Override
18 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
19 | if (String.class.equals(type)) {
20 | return new Converter() {
21 | @Override
22 | public String convert(ResponseBody value) throws IOException {
23 | return value.string();
24 | }
25 | };
26 | }
27 | return null;
28 | }
29 |
30 | @Override public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
31 | if (String.class.equals(type)) {
32 | return new Converter() {
33 | @Override
34 | public RequestBody convert(String value) throws IOException {
35 | return RequestBody.create(MEDIA_TYPE, value);
36 | }
37 | };
38 | }
39 | return null;
40 | }
41 | }
--------------------------------------------------------------------------------
/netlib/src/main/java/net/ljb/kt/interceptor/AddGlobalParamInterceptor.kt:
--------------------------------------------------------------------------------
1 | package net.ljb.kt.interceptor
2 |
3 | import net.ljb.kt.HttpConfig
4 | import okhttp3.Interceptor
5 | import okhttp3.Response
6 |
7 | /**
8 | * Author:Ljb
9 | * Time:2018/8/9
10 | * There is a lot of misery in life
11 | **/
12 | class AddGlobalParamInterceptor : Interceptor {
13 |
14 | override fun intercept(chain: Interceptor.Chain): Response {
15 |
16 | val paramMap = HttpConfig.getParam()
17 | val headerMap = HttpConfig.getHeader()
18 | val oldRequest = chain.request()
19 | val newRequestBuilder = oldRequest.newBuilder()
20 |
21 | // 添加公共Header
22 | if (headerMap != null && headerMap.isNotEmpty()) {
23 | val newHeaderBuilder = oldRequest.headers().newBuilder()
24 | headerMap.map { newHeaderBuilder.add(it.key, it.value) }
25 | newRequestBuilder.headers(newHeaderBuilder.build())
26 | }
27 |
28 | // 添加公共的Param
29 | if (paramMap != null && paramMap.isNotEmpty()) {
30 | val newUrlBuilder = oldRequest.url().newBuilder()
31 | newUrlBuilder.scheme(oldRequest.url().scheme())
32 | newUrlBuilder.host(oldRequest.url().host())
33 | paramMap.map { newUrlBuilder.addQueryParameter(it.key, it.value) }
34 | newRequestBuilder.url(newUrlBuilder.build())
35 | }
36 |
37 | // 新的请求
38 | val newRequest = newRequestBuilder
39 | .method(oldRequest.method(), oldRequest.body())
40 | .build()
41 | try {
42 | return chain.proceed(newRequest)
43 | } catch (e: Exception) {
44 | throw e
45 | }
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/netlib/src/main/java/net/ljb/kt/utils/NetLog.kt:
--------------------------------------------------------------------------------
1 | package net.ljb.kt.utils
2 |
3 | import android.util.Log
4 | import net.ljb.kt.HttpConfig
5 |
6 | object NetLog {
7 |
8 | /** 日志输出时的TAG */
9 | private const val mTag = "NetLog"
10 |
11 | /** 日志输出级别All */
12 | const val LEVEL_ALL = 10
13 |
14 | /** 日志输出级别NONE */
15 | const val LEVEL_NONE = 0
16 |
17 | /** 日志输出级别V */
18 | const val LEVEL_VERBOSE = 1
19 |
20 | /** 日志输出级别D */
21 | const val LEVEL_DEBUG = 2
22 |
23 | /** 日志输出级别I */
24 | const val LEVEL_INFO = 3
25 |
26 | /** 日志输出级别W */
27 | const val LEVEL_WARN = 4
28 |
29 | /** 日志输出级别E */
30 | const val LEVEL_ERROR = 5
31 |
32 | private val DEBUG_LEVEL = HttpConfig.getLogLv()
33 |
34 |
35 | /** 以级别为v 的形式输出LOG */
36 | fun v(msg: String?) {
37 | if (DEBUG_LEVEL >= LEVEL_VERBOSE) {
38 | Log.v(mTag, msg)
39 | }
40 | }
41 |
42 | /** 以级别为 d 的形式输出LOG */
43 | fun d(msg: String?) {
44 | if (DEBUG_LEVEL >= LEVEL_DEBUG) {
45 | Log.d(mTag, msg)
46 | }
47 | }
48 |
49 | /** 以级别为 i 的形式输出LOG */
50 | fun i(msg: String?) {
51 | if (DEBUG_LEVEL >= LEVEL_INFO) {
52 | Log.i(mTag, msg)
53 | }
54 | }
55 |
56 | /** 以级别为 w 的形式输出LOG */
57 | fun w(msg: String?) {
58 | if (DEBUG_LEVEL >= LEVEL_WARN) {
59 | Log.w(mTag, msg)
60 | }
61 | }
62 |
63 | /** 以级别为 w 的形式输出Throwable */
64 | fun w(tr: Throwable) {
65 | if (DEBUG_LEVEL >= LEVEL_WARN) {
66 | Log.w(mTag, "", tr)
67 | }
68 |
69 | }
70 |
71 | /** 以级别为 w 的形式输出LOG信息和Throwable */
72 | fun w(msg: String?, tr: Throwable) {
73 | if (DEBUG_LEVEL >= LEVEL_WARN && null != msg) {
74 | Log.w(mTag, msg, tr)
75 | }
76 | }
77 |
78 | /** 以级别为 e 的形式输出LOG */
79 | fun e(msg: String?) {
80 | if (DEBUG_LEVEL >= LEVEL_ERROR) {
81 | Log.e(mTag, msg)
82 | }
83 | }
84 |
85 | /** 以级别为 e 的形式输出Throwable */
86 | fun e(tr: Throwable) {
87 | if (DEBUG_LEVEL >= LEVEL_ERROR) {
88 | Log.e(mTag, "", tr)
89 | }
90 | }
91 |
92 | /** 以级别为 e 的形式输出LOG信息和Throwable */
93 | fun e(msg: String?, tr: Throwable) {
94 | if (DEBUG_LEVEL >= LEVEL_ERROR) {
95 | Log.e(mTag, msg, tr)
96 | }
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/netlib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | netLib
3 |
4 |
--------------------------------------------------------------------------------
/netlib/src/test/java/com/senyint/netlib/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.senyint.netlib;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/sh.exe.stackdump:
--------------------------------------------------------------------------------
1 | Stack trace:
2 | Frame Function Args
3 | 000FFFF9C48 0018005D19E (0018024212D, 00180223C26, 000FFFF9C48, 000FFFF8B40)
4 | 000FFFF9C48 001800463F9 (00000000000, 00000000000, 00000000000, 001005F0D3C)
5 | 000FFFF9C48 00180046432 (001802421E9, 000FFFF9AF8, 000FFFF9C48, 00000000000)
6 | 000FFFF9C48 001800A9EEF (00000000000, 00000000000, 00000000000, 00000000000)
7 | 000FFFF9C48 001800AA13D (000FFFF9C60, 00000000000, 00000000000, 00000000000)
8 | 000FFFF9ED0 001800AB3A4 (000FFFF9C60, 00000000000, 00000000000, 00000000000)
9 | End of stack trace
10 |
--------------------------------------------------------------------------------