├── .gitignore ├── Api.md ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── codekk │ │ ├── App.kt │ │ ├── Constant.kt │ │ ├── ext │ │ ├── Ext.kt │ │ ├── ExtEntity.kt │ │ ├── ExtNet.kt │ │ └── ExtSetting.kt │ │ ├── mvp │ │ ├── presenter │ │ │ ├── PresenterManager.kt │ │ │ └── impl │ │ │ │ ├── BlogListPresenterImpl.kt │ │ │ │ ├── JobListPresenterImpl.kt │ │ │ │ ├── OpPresenterImpl.kt │ │ │ │ ├── OpaPresenterImpl.kt │ │ │ │ ├── ReadmePresenterImpl.kt │ │ │ │ └── RecommendPresenterImpl.kt │ │ └── view │ │ │ └── ViewManager.kt │ │ └── ui │ │ ├── activity │ │ ├── MainActivity.kt │ │ ├── OpSearchActivity.kt │ │ ├── OpaSearchActivity.kt │ │ ├── ReadmeActivity.kt │ │ ├── RecommendSearchActivity.kt │ │ └── SettingActivity.kt │ │ ├── base │ │ ├── BaseActivity.kt │ │ ├── BaseFragment.kt │ │ ├── BaseListView.kt │ │ ├── BasePresenterImpl.kt │ │ ├── BaseView.kt │ │ ├── BaseViewBindActivity.kt │ │ ├── BaseViewBindFragment.kt │ │ └── ViewBindActivity.kt │ │ ├── fragment │ │ ├── BlogListFragment.kt │ │ ├── JobListFragment.kt │ │ ├── OpListFragment.kt │ │ ├── OpaListFragment.kt │ │ └── RecommendListFragment.kt │ │ └── widget │ │ ├── FlowText.kt │ │ ├── LoadMoreRecyclerView.kt │ │ └── SimpleMarkdownView.kt │ └── res │ ├── drawable-xxhdpi │ ├── ic_status_empty.png │ └── ic_status_error.png │ ├── drawable │ ├── ic_autorenew.xml │ ├── ic_blog.xml │ ├── ic_browser.xml │ ├── ic_check_box.xml │ ├── ic_check_box_outline.xml │ ├── ic_common.xml │ ├── ic_job.xml │ ├── ic_menu.xml │ ├── ic_notes.xml │ ├── ic_op.xml │ ├── ic_opa.xml │ ├── ic_recommend.xml │ ├── ic_search.xml │ ├── select_setting_box.xml │ └── shape_tag.xml │ ├── layout │ ├── activity_base.xml │ ├── activity_main.xml │ ├── activity_readme.xml │ ├── activity_setting.xml │ ├── drawer_header_layout.xml │ ├── item_blog_list.xml │ ├── item_job_list.xml │ ├── item_op_list.xml │ ├── item_opa_list.xml │ ├── item_recommend_list.xml │ ├── item_search.xml │ ├── item_setting.xml │ ├── item_setting_title.xml │ ├── layout_fragment.xml │ ├── layout_list.xml │ ├── layout_status_empty.xml │ ├── layout_status_error.xml │ └── layout_toolbar.xml │ ├── menu │ ├── drawerlayout_menu.xml │ ├── readme_menu.xml │ ├── search_menu.xml │ └── setting_menu.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── navigation │ └── main_navigation.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── blog_image.gif ├── codekk_image.gif ├── job_image.gif ├── op_image.gif └── opa_image.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | /.idea/ 3 | *build/ 4 | *.iml 5 | /local.properties 6 | /.gradle/ 7 | .DS_Store -------------------------------------------------------------------------------- /Api.md: -------------------------------------------------------------------------------- 1 | ## 进度 2 | 3 | BASE_URL = `http://api.codekk.com` 4 | 5 | 结构体: 6 | 7 | { 8 | "code": 0, // 状态码, 0 正常,其他异常 9 | "message": "", //提示信息 10 | "data": {} // 数据体 11 | } 12 | 13 | ## 开源项目 14 | 15 | - ~~获取开源项目列表 ~~ `/op/page/:page` ------------ `get` 16 | 17 | 可选参数: `type`,取值范围为:array、map、mix 18 | 19 | array、map 分别表示返回结果中 data 字段仅包含 projectArray 或 projectDateMap 20 | 21 | mix 表示都包含,默认为 array 22 | 23 | - ~~获取开源项目详情~~ `/op/detail/:id/readme` ------------ `get` 24 | 25 | - ~~搜索开源项目~~ `/op/search` ------------ `get` 26 | 27 | 可选参数:`text`表示搜索内容,`page`表示页数 28 | 29 | - 添加开源项目 `/op/add-project` ------------ `post` 30 | 31 | status:`TODO` 32 | 33 | - 检查开源项目是否存在 `/project/not-exist` ------------ `get` 34 | 35 | status:`TODO` 36 | 37 | - 添加标签 `/op/tagging` ------------ `post` 38 | 39 | status:`TODO` 40 | 41 | - 删除标签 `/op/untagging` ------------ `post` 42 | 43 | status:`TODO` 44 | 45 | - 根据提交者查询项目列表 `/op/committer/:committer/page/:page` ------------ `get` 46 | 47 | ## 源码解析 48 | 49 | - ~~获取源码解析文章列表~~ `/opa/page/:page` ------------ `get` 50 | 51 | - ~~获取源码解析文章详情~~ `/opa/detail/:id` ------------ `get` 52 | 53 | - ~~根据作者查询源码解析文章列表~~ `/opa/user/:userName/page/:page` ------------ `get` 54 | 55 | ## 职位内推 56 | 57 | - ~~获取职位内推文章列表~~ `/job/page/:page` ------------ `get` 58 | 59 | - ~~获取职位内推文章详情~~ `/job/detail/:id` ------------ `get` 60 | 61 | ## 博客文章 62 | 63 | - ~~获取博客文章列表~~ `/blog/page/:page` ------------ `get` 64 | 65 | - ~~获取单个博客文章详情~~ `/blog/detail/:id` ------------ `get` 66 | 67 | ## 个人笔记 68 | 69 | - 获取个人笔记列表 `/notes/user/:userName/page/:page` ------------ `get` 70 | 71 | ## 今日推荐 72 | 73 | - ~~获取今日推荐列表~~ `/recommend/page/:page` ------------ `get` 74 | 75 | - ~~根据推荐者查询推荐列表~~ `/recommend/user/:userName/page/:page` ------------ `get` 76 | 77 | ## 公共功能 78 | 79 | - 收藏某个内容 `/common/favorite` ------------ `post` 80 | 81 | - 取消收藏某个内容 `/common/unfavorite` ------------ `post` 82 | 83 | - 某个用户的收藏列表 `/favorite/user/:userName/page/:page` ------------ `get` 84 | 85 | - (未开放)添加标签 `/common/tagging` 86 | 87 | status:`TODO` 88 | 89 | - (未开放)删除某个标签 `/common/untagging` 90 | 91 | status:`TODO` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ViewBind 使用 2 | 3 | 1.必须为include标签命名一个id,而include里面的View使用则是viewBind.includeBind.id,麻烦 4 | 5 | 2.BK是自动生成的文件,ViewBind也是,不过是由IDE深度集成支持的 6 | 7 | 3.和DataBinding比较,个人觉得区别不大...仅仅是xml文件不使用标签的形式而已,至少目前是这样的... 8 | 9 | 4.和kotlin一样,IDE深度集成的优点和BK相比就是就算不重新Build也支持viewBind.id,命名上也是驼峰式命名,如果不适应kotlin在xml中id命名驼峰式,可以用ViewBind 10 | 11 | 5.谈不上优劣,目前个人觉得viewBind.id有点麻烦,相比kotlin来说,kotlin创建一个Map在类中还是比较方面... 12 | 13 | 6.kotlin的问题是如果xml多了命名有重复,容易引入错误的布局类,而ViewBind不存在这种情况,因为它是为每个布局文件都生成了一个类 14 | 15 | 目前已完成 16 | 17 | ![](https://github.com/7449/codeKK-Android/blob/master/screenshots/codekk_image.gif) 18 | 19 | * 查看开源项目,搜索开源项目 20 | 21 | ![](https://github.com/7449/codeKK-Android/blob/master/screenshots/op_image.gif) 22 | 23 | 24 | * 查看源码解析,搜索源码解析 25 | 26 | ![](https://github.com/7449/codeKK-Android/blob/master/screenshots/opa_image.gif) 27 | 28 | * 查看博客文章,查看职位内推 29 | 30 | ![](https://github.com/7449/codeKK-Android/blob/master/screenshots/blog_image.gif) 31 | 32 | ![](https://github.com/7449/codeKK-Android/blob/master/screenshots/job_image.gif) 33 | 34 | * 查看今日推荐,搜索今日推荐 35 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | 5 | android { 6 | compileSdkVersion rootProject.compileSdkVersion 7 | defaultConfig { 8 | applicationId rootProject.applicationId 9 | minSdkVersion rootProject.minSdkVersion 10 | targetSdkVersion rootProject.targetSdkVersion 11 | versionCode rootProject.versionCode 12 | versionName rootProject.versionName 13 | multiDexEnabled true 14 | } 15 | buildFeatures { 16 | viewBinding = true 17 | dataBinding = true 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | kotlinOptions { 26 | jvmTarget = 1.8 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation "androidx.appcompat:appcompat:$appcompatVersion" 32 | implementation "com.google.android.material:material:$materialVersion" 33 | implementation "androidx.core:core-ktx:$ktxVersion" 34 | 35 | implementation "com.ydevelop:rv-adapter:$xAdapterVersion" 36 | implementation "com.ydevelop:status-layout:$statusVersion" 37 | implementation "com.ydevelop:rxNetWork:$rxNetWorkVersion" 38 | implementation "io.reactivex.rxjava2:rxandroid:$rxandroidVersion" 39 | implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" 40 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" 41 | implementation "com.squareup.okhttp3:logging-interceptor:$logVersion" 42 | 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 44 | implementation "com.afollestad.material-dialogs:input:$materialDialogVersion" 45 | implementation "com.afollestad.material-dialogs:core:$materialDialogVersion" 46 | implementation "com.github.mukeshsolanki:MarkdownView-Android:$markdownViewVersion" 47 | implementation "com.google.android:flexbox:$flexboxVersion" 48 | 49 | implementation "androidx.legacy:legacy-support-v4:$legacysupportv4Version" 50 | implementation "androidx.constraintlayout:constraintlayout:$constranintlayoutVersion" 51 | implementation "androidx.navigation:navigation-fragment:$navigationVersion" 52 | implementation "androidx.navigation:navigation-ui:$navigationVersion" 53 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleExtensionsVersion" 54 | implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" 55 | implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" 56 | 57 | implementation "org.jetbrains.anko:anko:$ankoVersion" 58 | } 59 | -------------------------------------------------------------------------------- /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:\software\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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 33 | 36 | 39 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/App.kt: -------------------------------------------------------------------------------- 1 | package com.codekk 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import android.content.Context 6 | import com.codekk.ext.Api 7 | import io.reactivex.network.RxNetWork 8 | import okhttp3.Interceptor 9 | import okhttp3.logging.HttpLoggingInterceptor 10 | import retrofit2.converter.gson.GsonConverterFactory 11 | 12 | /** 13 | * by y on 2017/5/16 14 | */ 15 | class App : Application() { 16 | 17 | override fun onCreate() { 18 | super.onCreate() 19 | context = applicationContext 20 | RxNetWork.initOption { 21 | superBaseUrl { Api.BASE_API } 22 | superConverterFactory { GsonConverterFactory.create() } 23 | superInterceptor { 24 | val logInterceptor = HttpLoggingInterceptor() 25 | logInterceptor.level = HttpLoggingInterceptor.Level.BODY 26 | val arrayList = ArrayList() 27 | arrayList.add(logInterceptor) 28 | arrayList 29 | } 30 | } 31 | } 32 | 33 | companion object { 34 | @SuppressLint("StaticFieldLeak") 35 | private lateinit var context: Context 36 | 37 | val instance: App 38 | get() = context as App 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/Constant.kt: -------------------------------------------------------------------------------- 1 | package com.codekk 2 | 3 | /** 4 | * by y on 2017/5/16 5 | */ 6 | object Constant { 7 | const val TYPE_OP = 0 8 | const val TYPE_JOB = 1 9 | const val TYPE_BLOG = 2 10 | const val TYPE_OPA = 3 11 | const val TYPE_RECOMMEND = 4 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ext/Ext.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ext 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | import android.view.View 7 | import androidx.annotation.ColorInt 8 | import androidx.annotation.StringRes 9 | import com.afollestad.materialdialogs.MaterialDialog 10 | import com.afollestad.materialdialogs.input.input 11 | import com.android.status.layout.StatusLayout 12 | import com.codekk.R 13 | import com.codekk.ui.widget.FlowText 14 | import com.google.android.flexbox.FlexboxLayout 15 | import com.google.android.material.snackbar.Snackbar 16 | 17 | fun Activity.openSearch(@StringRes stringId: Int, dialogListener: (input: String) -> Unit) { 18 | MaterialDialog(this).show { 19 | title(res = R.string.search_dialog_title) 20 | input(hintRes = stringId) { _, text -> 21 | dialogListener(text.toString()) 22 | } 23 | } 24 | } 25 | 26 | fun OpListBean.tags(): ArrayList? { 27 | val arrayList = ArrayList() 28 | tags?.let { 29 | for (opTagsBean in it) { 30 | arrayList.add(opTagsBean.name) 31 | } 32 | return arrayList 33 | } 34 | return null 35 | } 36 | 37 | fun FlexboxLayout.tags(tags: List?, click: (tag: String) -> Unit) { 38 | removeAllViews() 39 | tags?.let { 40 | for (element in tags) { 41 | val flowText = FlowText(context) 42 | flowText.text = element 43 | addView(flowText) 44 | flowText.setOnClickListener { click.invoke(element) } 45 | } 46 | } 47 | } 48 | 49 | fun View.snackBar(@StringRes stringRes: Int) { 50 | Snackbar.make(this, stringRes, Snackbar.LENGTH_SHORT).show() 51 | } 52 | 53 | fun View.snackBar(@StringRes stringRes: Int, @ColorInt color: Int) { 54 | Snackbar.make(this, stringRes, Snackbar.LENGTH_SHORT) 55 | .setActionTextColor(color) 56 | .show() 57 | } 58 | 59 | fun StatusLayout.error() = run { status = StatusLayout.ERROR } 60 | 61 | fun StatusLayout.empty() = run { status = StatusLayout.EMPTY } 62 | 63 | fun StatusLayout.success() = run { status = StatusLayout.SUCCESS } 64 | 65 | fun Context.sharedPreferences(): SharedPreferences = applicationContext.getSharedPreferences("codeKK", Context.MODE_PRIVATE) 66 | 67 | fun Context.opTagBoolean(): Boolean = sharedPreferences().getBoolean("op_tag", true) 68 | fun Context.opTagBoolean(value: Boolean) = sharedPreferences().edit().putBoolean("op_tag", value).apply() 69 | 70 | fun Context.opUriWebBoolean(): Boolean = sharedPreferences().getBoolean("op_uri_web", true) 71 | fun Context.opUriWebBoolean(value: Boolean) = sharedPreferences().edit().putBoolean("op_uri_web", value).commit() 72 | 73 | fun Context.opaTagBoolean(): Boolean = sharedPreferences().getBoolean("opa_tag", true) 74 | fun Context.opaTagBoolean(value: Boolean) = sharedPreferences().edit().putBoolean("opa_tag", value).apply() 75 | 76 | fun Context.opaUriWebBoolean(): Boolean = sharedPreferences().getBoolean("opa_uri_web", true) 77 | fun Context.opaUriWebBoolean(value: Boolean) = sharedPreferences().edit().putBoolean("opa_uri_web", value).apply() 78 | 79 | fun Context.blogTagBoolean(): Boolean = sharedPreferences().getBoolean("blog_tag", true) 80 | fun Context.blogTagBoolean(value: Boolean) = sharedPreferences().edit().putBoolean("blog_tag", value).apply() -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ext/ExtEntity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ext 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class BaseModel(val message: String = "", val code: Int = 0, val data: T) 6 | 7 | data class OpListModel(@SerializedName("projectArray") val opList: List) 8 | 9 | data class OpListBean(@SerializedName("_id") val id: String, 10 | val desc: String, 11 | val authorUrl: String, 12 | val projectName: String, 13 | val projectUrl: String, 14 | val authorName: String, 15 | val tags: List?) 16 | 17 | data class OpTagsBean(val name: String) 18 | 19 | data class OpaListModel(@SerializedName("summaryArray") val opaList: List) 20 | 21 | data class OpaListBean(@SerializedName("_id") val id: String, 22 | val summary: String, 23 | val title: String, 24 | val projectUrl: String, 25 | val projectName: String, 26 | val authorName: String, 27 | val tagList: List?) 28 | 29 | data class JobListModel(@SerializedName("summaryArray") val jobList: List) 30 | 31 | data class JobListBean(@SerializedName("_id") val id: String, 32 | val summary: String, 33 | val projectUrl: String, 34 | val projectName: String, 35 | val authorName: String, 36 | val authorCity: String, 37 | val expiredTime: String) 38 | 39 | data class BlogListModel(@SerializedName("summaryArray") val blogList: List) 40 | 41 | data class BlogListBean(@SerializedName("_id") val id: String, 42 | val title: String, 43 | val summary: String, 44 | val projectUrl: String, 45 | val projectName: String, 46 | val authorName: String, 47 | val tagList: List?) 48 | 49 | data class RecommendListModel(@SerializedName("recommendArray") val recommendList: List) 50 | 51 | data class RecommendListBean(val url: String, 52 | val title: String, 53 | val desc: String) 54 | 55 | data class ReadmeModel(val content: String) -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ext/ExtNet.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ext 2 | 3 | import com.codekk.App 4 | import io.reactivex.Observable 5 | import io.reactivex.annotations.NonNull 6 | import io.reactivex.functions.Function 7 | import org.jetbrains.anko.toast 8 | import retrofit2.http.GET 9 | import retrofit2.http.Path 10 | import retrofit2.http.Query 11 | 12 | object Api { 13 | const val BASE_API = "https://api.codekk.com/" 14 | const val OP_LIST_URL = "op/page/" //获取开源项目 15 | const val OP_DETAIL_URL = "op/detail/" //获取单个开源项目 ReadMe 16 | const val OP_SEARCH_URL = "op/search" //搜索开源项目 17 | const val OPA_LIST_URL = "opa/page/" //获取源码解析文章列表 18 | const val OPA_DETAIL_URL = "opa/detail/" //获取单个源码解析文章详情 19 | const val OPA_SEARCH_URL = "opa/user/" 20 | const val JOB_LIST_URL = "job/page/" //获取职位内推文章列表 21 | const val JOB_DETAIL_URL = "job/detail/" //获取单个职位内推文章详情 22 | const val BLOG_LIST_URL = "blog/page/" //获取博客文章列表 23 | const val BLOG_DETAIL_URL = "blog/detail/" //获取单个博客文章详情 24 | const val RECOMMEND_LIST_URL = "recommend/page/" //获取今日推荐列表 25 | const val RECOMMEND_SEARCH_URL = "recommend/user/" //根据推荐者查询推荐列表 26 | } 27 | 28 | interface OpService { 29 | @GET("${Api.OP_LIST_URL}{page}") 30 | fun getOpList(@Path("page") page: Int, @Query("type") type: String = "array"): Observable> 31 | 32 | @GET("${Api.OP_DETAIL_URL}{id}/readme") 33 | fun getOpDetail(@Path("id") id: String): Observable> 34 | 35 | @GET(Api.OP_SEARCH_URL) 36 | fun getOpSearch(@Query("text") text: String, @Query("page") page: Int): Observable> 37 | } 38 | 39 | interface OpaService { 40 | @GET("${Api.OPA_LIST_URL}{page}") 41 | fun getOpaList(@Path("page") page: Int): Observable> 42 | 43 | @GET("${Api.OPA_DETAIL_URL}{id}") 44 | fun getOpaDetail(@Path("id") id: String): Observable> 45 | 46 | @GET("${Api.OPA_SEARCH_URL}{name}/page/{page}") 47 | fun getOpaSearch(@Path("name") name: String, @Path("page") page: Int): Observable> 48 | } 49 | 50 | interface JobService { 51 | @GET("${Api.JOB_LIST_URL}{page}") 52 | fun getJobList(@Path("page") page: Int): Observable> 53 | 54 | @GET("${Api.JOB_DETAIL_URL}{id}") 55 | fun getJobDetail(@Path("id") id: String): Observable> 56 | } 57 | 58 | interface BlogService { 59 | @GET("${Api.BLOG_LIST_URL}{page}") 60 | fun getBlogList(@Path("page") page: Int): Observable> 61 | 62 | @GET("${Api.BLOG_DETAIL_URL}{id}") 63 | fun getBlogDetail(@Path("id") id: String): Observable> 64 | } 65 | 66 | interface RecommendService { 67 | @GET("${Api.RECOMMEND_LIST_URL}{page}") 68 | fun getRecommendList(@Path("page") page: Int): Observable> 69 | 70 | @GET("${Api.RECOMMEND_SEARCH_URL}{user}/page/{page}") 71 | fun getRecommendSearch(@Path("user") name: String, @Path("page") page: Int): Observable> 72 | } 73 | 74 | class NetException(code: Int, message: String) : RuntimeException() { 75 | init { 76 | App.instance.toast("$code---$message") 77 | } 78 | } 79 | 80 | class NetFunc : Function, T> { 81 | override fun apply(@NonNull model: BaseModel): T { 82 | if (model.code != 0) { 83 | throw NetException(model.code, model.message) 84 | } 85 | return model.data 86 | } 87 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ext/ExtSetting.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ext 2 | 3 | import android.view.View 4 | import com.codekk.ui.fragment.BlogListFragment 5 | import com.codekk.ui.fragment.OpListFragment 6 | import com.codekk.ui.fragment.OpaListFragment 7 | import com.xadapter.listener.XMultiCallBack 8 | import io.reactivex.network.RxBus 9 | 10 | object ExtSetting { 11 | const val TYPE_TITLE = 0 12 | const val TYPE_ITEM = 1 13 | } 14 | 15 | data class SettingItem( 16 | val message: String = "", 17 | val itemMultiType: Int = ExtSetting.TYPE_TITLE, 18 | val itemMultiPosition: Int = XMultiCallBack.NO_CLICK_POSITION 19 | ) : XMultiCallBack { 20 | override val itemType: Int = itemMultiType 21 | override val position: Int = itemMultiPosition 22 | } 23 | 24 | fun View.settingItemClick(position: Int, boolean: Boolean) { 25 | when (position) { 26 | 0 -> { 27 | context.opTagBoolean(!boolean) 28 | RxBus.postBus(OpListFragment::class.java.simpleName, OpListFragment::class.java.simpleName) 29 | } 30 | 1 -> { 31 | context.opUriWebBoolean(!boolean) 32 | RxBus.postBus(OpListFragment::class.java.simpleName, OpListFragment::class.java.simpleName) 33 | } 34 | 2 -> { 35 | context.opaTagBoolean(!boolean) 36 | RxBus.postBus(OpaListFragment::class.java.simpleName, OpaListFragment::class.java.simpleName) 37 | } 38 | 3 -> { 39 | context.opaUriWebBoolean(!boolean) 40 | RxBus.postBus(OpaListFragment::class.java.simpleName, OpaListFragment::class.java.simpleName) 41 | } 42 | 4 -> { 43 | context.blogTagBoolean(!boolean) 44 | RxBus.postBus(BlogListFragment::class.java.simpleName, BlogListFragment::class.java.simpleName) 45 | } 46 | } 47 | } 48 | 49 | fun initItem(): ArrayList { 50 | val list = ArrayList() 51 | list.add(SettingItem("开源项目")) 52 | list.add(SettingItem("显示TAG", ExtSetting.TYPE_ITEM, 0)) 53 | list.add(SettingItem("github地址是否设置为超链接", ExtSetting.TYPE_ITEM, 1)) 54 | list.add(SettingItem("源码解析")) 55 | list.add(SettingItem("显示TAG", ExtSetting.TYPE_ITEM, 2)) 56 | list.add(SettingItem("解析对象连接是否设置为超链接", ExtSetting.TYPE_ITEM, 3)) 57 | list.add(SettingItem("博客文章")) 58 | list.add(SettingItem("显示TAG", ExtSetting.TYPE_ITEM, 4)) 59 | return list 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/PresenterManager.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter 2 | 3 | interface BlogListPresenter { 4 | fun netWorkRequest(page: Int) 5 | } 6 | 7 | interface JobListPresenter { 8 | fun netWorkRequest(page: Int) 9 | } 10 | 11 | interface RecommendPresenter { 12 | fun netWorkRequestList(page: Int) 13 | fun netWorkRequestSearch(name: String, page: Int) 14 | } 15 | 16 | interface OpaPresenter { 17 | fun netWorkRequestList(page: Int) 18 | fun netWorkRequestSearch(text: String, page: Int) 19 | } 20 | 21 | interface OpPresenter { 22 | fun netWorkRequestList(page: Int) 23 | fun netWorkRequestSearch(text: String, page: Int) 24 | } 25 | 26 | interface ReadmePresenter { 27 | fun netWorkRequest(id: String, type: Int) 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/impl/BlogListPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter.impl 2 | 3 | import com.codekk.ext.BlogListModel 4 | import com.codekk.ext.BlogService 5 | import com.codekk.ext.NetFunc 6 | import com.codekk.mvp.presenter.BlogListPresenter 7 | import com.codekk.mvp.view.BlogListView 8 | import com.codekk.ui.base.BasePresenterImpl 9 | import io.reactivex.network.RxNetWork 10 | import io.reactivex.network.cancelTag 11 | import io.reactivex.network.getApi 12 | 13 | /** 14 | * by y on 2017/5/19 15 | */ 16 | class BlogListPresenterImpl(view: BlogListView) : BasePresenterImpl(view), BlogListPresenter { 17 | 18 | override fun netWorkRequest(page: Int) { 19 | RxNetWork 20 | .observable(BlogService::class.java) 21 | .getBlogList(page) 22 | .cancelTag(javaClass.simpleName) 23 | .map(NetFunc()) 24 | .getApi(javaClass.simpleName, this) 25 | } 26 | 27 | override fun onDestroy() { 28 | super.onDestroy() 29 | RxNetWork.cancelTag(javaClass.simpleName) 30 | } 31 | 32 | override fun onNetWorkSuccess(data: BlogListModel) { 33 | if (data.blogList.isEmpty()) { 34 | view?.noMore() 35 | } else { 36 | super.onNetWorkSuccess(data) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/impl/JobListPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter.impl 2 | 3 | import com.codekk.ext.JobListModel 4 | import com.codekk.ext.JobService 5 | import com.codekk.ext.NetFunc 6 | import com.codekk.mvp.presenter.JobListPresenter 7 | import com.codekk.mvp.view.JobListView 8 | import com.codekk.ui.base.BasePresenterImpl 9 | import io.reactivex.network.RxNetWork 10 | import io.reactivex.network.cancelTag 11 | import io.reactivex.network.getApi 12 | 13 | /** 14 | * by y on 2017/5/18. 15 | */ 16 | class JobListPresenterImpl(view: JobListView) : BasePresenterImpl(view), JobListPresenter { 17 | 18 | override fun netWorkRequest(page: Int) { 19 | RxNetWork 20 | .observable(JobService::class.java) 21 | .getJobList(page) 22 | .cancelTag(javaClass.simpleName) 23 | .map(NetFunc()) 24 | .getApi(javaClass.simpleName, this) 25 | } 26 | 27 | override fun onDestroy() { 28 | super.onDestroy() 29 | RxNetWork.cancelTag(javaClass.simpleName) 30 | } 31 | 32 | override fun onNetWorkSuccess(data: JobListModel) { 33 | if (data.jobList.isEmpty()) { 34 | view?.noMore() 35 | } else { 36 | super.onNetWorkSuccess(data) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/impl/OpPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter.impl 2 | 3 | import com.codekk.ext.NetFunc 4 | import com.codekk.ext.OpListModel 5 | import com.codekk.ext.OpService 6 | import com.codekk.mvp.presenter.OpPresenter 7 | import com.codekk.mvp.view.OpListView 8 | import com.codekk.ui.base.BasePresenterImpl 9 | import io.reactivex.network.RxNetWork 10 | import io.reactivex.network.cancelTag 11 | import io.reactivex.network.getApi 12 | 13 | /** 14 | * by y on 2017/5/16 15 | */ 16 | class OpPresenterImpl(view: OpListView) : BasePresenterImpl(view), OpPresenter { 17 | 18 | override fun netWorkRequestList(page: Int) { 19 | RxNetWork 20 | .observable(OpService::class.java) 21 | .getOpList(page) 22 | .cancelTag(javaClass.simpleName) 23 | .map(NetFunc()) 24 | .getApi(javaClass.simpleName, this) 25 | } 26 | 27 | override fun netWorkRequestSearch(text: String, page: Int) { 28 | RxNetWork 29 | .observable(OpService::class.java) 30 | .getOpSearch(text, page) 31 | .cancelTag(javaClass.simpleName) 32 | .map(NetFunc()) 33 | .getApi(javaClass.simpleName, this) 34 | } 35 | 36 | override fun onDestroy() { 37 | super.onDestroy() 38 | RxNetWork.cancelTag(javaClass.simpleName) 39 | } 40 | 41 | override fun onNetWorkSuccess(data: OpListModel) { 42 | if (data.opList.isEmpty()) { 43 | view?.noMore() 44 | } else { 45 | super.onNetWorkSuccess(data) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/impl/OpaPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter.impl 2 | 3 | import com.codekk.ext.NetFunc 4 | import com.codekk.ext.OpaListModel 5 | import com.codekk.ext.OpaService 6 | import com.codekk.mvp.presenter.OpaPresenter 7 | import com.codekk.mvp.view.OpaListView 8 | import com.codekk.ui.base.BasePresenterImpl 9 | import io.reactivex.network.RxNetWork 10 | import io.reactivex.network.cancelTag 11 | import io.reactivex.network.getApi 12 | 13 | /** 14 | * by y on 2017/5/18 15 | */ 16 | class OpaPresenterImpl(view: OpaListView) : BasePresenterImpl(view), OpaPresenter { 17 | 18 | override fun netWorkRequestList(page: Int) { 19 | RxNetWork 20 | .observable(OpaService::class.java) 21 | .getOpaList(page) 22 | .cancelTag(javaClass.simpleName) 23 | .map(NetFunc()) 24 | .getApi(javaClass.simpleName, this) 25 | } 26 | 27 | override fun netWorkRequestSearch(text: String, page: Int) { 28 | RxNetWork 29 | .observable(OpaService::class.java) 30 | .getOpaSearch(text, page) 31 | .cancelTag(javaClass.simpleName) 32 | .map(NetFunc()) 33 | .getApi(javaClass.simpleName, this) 34 | } 35 | 36 | override fun onDestroy() { 37 | super.onDestroy() 38 | RxNetWork.cancelTag(javaClass.simpleName) 39 | } 40 | 41 | override fun onNetWorkSuccess(data: OpaListModel) { 42 | if (data.opaList.isEmpty()) { 43 | view?.noMore() 44 | } else { 45 | super.onNetWorkSuccess(data) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/impl/ReadmePresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter.impl 2 | 3 | import com.codekk.Constant 4 | import com.codekk.ext.* 5 | import com.codekk.mvp.presenter.ReadmePresenter 6 | import com.codekk.mvp.view.ReadmeView 7 | import com.codekk.ui.base.BasePresenterImpl 8 | import io.reactivex.network.RxNetWork 9 | import io.reactivex.network.cancelTag 10 | import io.reactivex.network.getApi 11 | 12 | /** 13 | * by y on 2017/5/16 14 | */ 15 | class ReadmePresenterImpl(view: ReadmeView) : BasePresenterImpl(view), ReadmePresenter { 16 | 17 | override fun netWorkRequest(id: String, type: Int) { 18 | when (type) { 19 | Constant.TYPE_JOB -> RxNetWork 20 | .observable(JobService::class.java) 21 | .getJobDetail(id) 22 | .cancelTag(javaClass.simpleName) 23 | .map(NetFunc()) 24 | .getApi(javaClass.simpleName, this) 25 | Constant.TYPE_OP -> RxNetWork 26 | .observable(OpService::class.java) 27 | .getOpDetail(id) 28 | .cancelTag(javaClass.simpleName) 29 | .map(NetFunc()) 30 | .getApi(javaClass.simpleName, this) 31 | Constant.TYPE_BLOG -> RxNetWork 32 | .observable(BlogService::class.java) 33 | .getBlogDetail(id) 34 | .cancelTag(javaClass.simpleName) 35 | .map(NetFunc()) 36 | .getApi(javaClass.simpleName, this) 37 | Constant.TYPE_OPA -> RxNetWork 38 | .observable(OpaService::class.java) 39 | .getOpaDetail(id) 40 | .cancelTag(javaClass.simpleName) 41 | .map(NetFunc()) 42 | .getApi(javaClass.simpleName, this) 43 | else -> view?.loadWebViewUrl() 44 | } 45 | } 46 | 47 | override fun onDestroy() { 48 | super.onDestroy() 49 | RxNetWork.cancelTag(javaClass.simpleName) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/presenter/impl/RecommendPresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.presenter.impl 2 | 3 | import com.codekk.ext.NetFunc 4 | import com.codekk.ext.RecommendListModel 5 | import com.codekk.ext.RecommendService 6 | import com.codekk.mvp.presenter.RecommendPresenter 7 | import com.codekk.mvp.view.RecommendListView 8 | import com.codekk.ui.base.BasePresenterImpl 9 | import io.reactivex.network.RxNetWork 10 | import io.reactivex.network.cancelTag 11 | import io.reactivex.network.getApi 12 | 13 | /** 14 | * by y on 2017/5/20. 15 | */ 16 | class RecommendPresenterImpl(view: RecommendListView) : BasePresenterImpl(view), RecommendPresenter { 17 | 18 | override fun netWorkRequestList(page: Int) { 19 | RxNetWork 20 | .observable(RecommendService::class.java) 21 | .getRecommendList(page) 22 | .cancelTag(javaClass.simpleName) 23 | .map(NetFunc()) 24 | .getApi(javaClass.simpleName, this) 25 | } 26 | 27 | override fun netWorkRequestSearch(name: String, page: Int) { 28 | RxNetWork 29 | .observable(RecommendService::class.java) 30 | .getRecommendSearch(name, page) 31 | .cancelTag(javaClass.simpleName) 32 | .map(NetFunc()) 33 | .getApi(javaClass.simpleName, this) 34 | } 35 | 36 | override fun onDestroy() { 37 | super.onDestroy() 38 | RxNetWork.cancelTag(javaClass.simpleName) 39 | } 40 | 41 | override fun onNetWorkSuccess(data: RecommendListModel) { 42 | if (data.recommendList.isEmpty()) { 43 | view?.noMore() 44 | } else { 45 | super.onNetWorkSuccess(data) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/mvp/view/ViewManager.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.mvp.view 2 | 3 | import com.codekk.ext.* 4 | import com.codekk.ui.base.BaseListView 5 | import com.codekk.ui.base.BaseView 6 | 7 | interface BlogListView : BaseListView 8 | 9 | interface JobListView : BaseListView 10 | 11 | interface OpaListView : BaseListView 12 | 13 | interface ReadmeView : BaseView { 14 | fun loadWebViewUrl() 15 | } 16 | 17 | interface OpListView : BaseListView 18 | 19 | interface RecommendListView : BaseListView -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.activity 2 | 3 | import android.os.Bundle 4 | import android.view.Menu 5 | import android.view.MenuItem 6 | import androidx.core.view.GravityCompat 7 | import androidx.navigation.findNavController 8 | import androidx.navigation.ui.AppBarConfiguration 9 | import androidx.navigation.ui.navigateUp 10 | import androidx.navigation.ui.setupActionBarWithNavController 11 | import androidx.navigation.ui.setupWithNavController 12 | import com.codekk.R 13 | import com.codekk.databinding.ActivityMainBinding 14 | import com.codekk.ui.base.ViewBindActivity 15 | import org.jetbrains.anko.startActivity 16 | 17 | class MainActivity : ViewBindActivity() { 18 | 19 | private val appBarConfiguration by lazy { AppBarConfiguration(setOf(R.id.op, R.id.opa, R.id.job, R.id.blog, R.id.recommend), viewBind.drawerLayout) } 20 | 21 | override fun initViewBind(): ActivityMainBinding { 22 | return ActivityMainBinding.inflate(layoutInflater) 23 | } 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | setSupportActionBar(viewBind.toolbarRoot.toolbar) 28 | viewBind.toolbarRoot.toolbar.setNavigationIcon(R.drawable.ic_menu) 29 | viewBind.toolbarRoot.toolbar.setNavigationOnClickListener { viewBind.drawerLayout.openDrawer(GravityCompat.START) } 30 | val navController = findNavController(R.id.hostFragment) 31 | setupActionBarWithNavController(navController, appBarConfiguration) 32 | viewBind.navigationview.setupWithNavController(navController) 33 | } 34 | 35 | override fun onSupportNavigateUp(): Boolean { 36 | val navController = findNavController(R.id.hostFragment) 37 | return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() 38 | } 39 | 40 | override fun onBackPressed() { 41 | if (viewBind.drawerLayout.isDrawerOpen(GravityCompat.START)) { 42 | viewBind.drawerLayout.closeDrawers() 43 | } else { 44 | super.onBackPressed() 45 | } 46 | } 47 | 48 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 49 | menuInflater.inflate(R.menu.setting_menu, menu) 50 | return true 51 | } 52 | 53 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 54 | when (item.itemId) { 55 | R.id.setting -> startActivity() 56 | } 57 | return super.onOptionsItemSelected(item) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/activity/OpSearchActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.activity 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import com.codekk.R 6 | import com.codekk.databinding.LayoutFragmentBinding 7 | import com.codekk.ui.base.ViewBindActivity 8 | import com.codekk.ui.fragment.OpListFragment 9 | 10 | /** 11 | * by y on 2017/5/17 12 | */ 13 | class OpSearchActivity : ViewBindActivity() { 14 | 15 | companion object { 16 | const val TEXT_KEY = "text" 17 | } 18 | 19 | private val text by lazy { intent?.extras?.getString(TEXT_KEY, "").orEmpty() } 20 | 21 | override fun initViewBind(): LayoutFragmentBinding { 22 | return LayoutFragmentBinding.inflate(layoutInflater) 23 | } 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | viewBind.toolbarRoot.toolbar.title = text 28 | setSupportActionBar(viewBind.toolbarRoot.toolbar) 29 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 30 | supportFragmentManager.beginTransaction().replace(R.id.fragment, OpListFragment.get(text)).commitAllowingStateLoss() 31 | } 32 | 33 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 34 | if (item.itemId == android.R.id.home) { 35 | finish() 36 | } 37 | return super.onOptionsItemSelected(item) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/activity/OpaSearchActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.activity 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.text.util.Linkify 6 | import android.view.View 7 | import androidx.appcompat.widget.AppCompatTextView 8 | import androidx.recyclerview.widget.LinearLayoutManager 9 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 10 | import com.codekk.Constant 11 | import com.codekk.R 12 | import com.codekk.databinding.LayoutListBinding 13 | import com.codekk.ext.* 14 | import com.codekk.mvp.presenter.impl.OpaPresenterImpl 15 | import com.codekk.mvp.view.OpaListView 16 | import com.codekk.ui.base.BaseViewBindActivity 17 | import com.codekk.ui.widget.LoadMoreRecyclerView 18 | import com.google.android.flexbox.FlexboxLayout 19 | import com.xadapter.* 20 | import com.xadapter.adapter.XAdapter 21 | import com.xadapter.holder.XViewHolder 22 | import org.jetbrains.anko.startActivity 23 | 24 | /** 25 | * by y on 2017/5/18. 26 | */ 27 | class OpaSearchActivity : BaseViewBindActivity(), OpaListView, LoadMoreRecyclerView.LoadMoreListener, SwipeRefreshLayout.OnRefreshListener { 28 | 29 | private val text by lazy { bundle?.getString(TEXT_KEY, "").orEmpty() } 30 | private var page = 1 31 | 32 | private val mAdapter by lazy { XAdapter() } 33 | 34 | override fun initViewBind(): LayoutListBinding { 35 | return LayoutListBinding.inflate(layoutInflater) 36 | } 37 | 38 | override fun initPresenter(): OpaPresenterImpl { 39 | return OpaPresenterImpl(this) 40 | } 41 | 42 | override fun initCreate(savedInstanceState: Bundle?) { 43 | baseViewBind.toolbarRoot.toolbar.title = text 44 | setSupportActionBar(baseViewBind.toolbarRoot.toolbar) 45 | viewBind.recyclerView.setHasFixedSize(true) 46 | viewBind.recyclerView.layoutManager = LinearLayoutManager(this) 47 | 48 | viewBind.recyclerView.adapter = mAdapter 49 | .setItemLayoutId(R.layout.item_opa_list) 50 | .setOnItemClickListener { _, _, info -> 51 | startActivity( 52 | ReadmeActivity.TYPE to arrayOf(info.id, info.projectName, info.projectUrl), 53 | ReadmeActivity.KEY to Constant.TYPE_OPA 54 | ) 55 | } 56 | .setOnBind { holder, _, entity -> onXBind(holder, entity) } 57 | 58 | viewBind.refreshLayout.setOnRefreshListener(this) 59 | viewBind.refreshLayout.post { this.onRefresh() } 60 | } 61 | 62 | override fun clickNetWork() { 63 | super.clickNetWork() 64 | if (!viewBind.refreshLayout.isRefreshing) { 65 | onRefresh() 66 | } 67 | } 68 | 69 | override fun showProgress() { 70 | viewBind.refreshLayout.isRefreshing = true 71 | } 72 | 73 | override fun hideProgress() { 74 | viewBind.refreshLayout.isRefreshing = false 75 | } 76 | 77 | override fun onRefresh() { 78 | baseViewBind.statusLayout.success() 79 | mPresenter.netWorkRequestSearch(text, page = 1) 80 | } 81 | 82 | override fun onLoadMore() { 83 | if (viewBind.refreshLayout.isRefreshing) { 84 | return 85 | } 86 | mPresenter.netWorkRequestSearch(text, page) 87 | } 88 | 89 | override fun netWorkSuccess(entity: OpaListModel) { 90 | if (page == 1) { 91 | mAdapter.removeAll() 92 | } 93 | ++page 94 | mAdapter.addAll(entity.opaList) 95 | } 96 | 97 | override fun netWorkError(throwable: Throwable) { 98 | if (page == 1) { 99 | baseViewBind.statusLayout.error() 100 | mAdapter.removeAll() 101 | } else { 102 | baseViewBind.statusLayout.snackBar(R.string.net_error) 103 | } 104 | } 105 | 106 | override fun noMore() { 107 | if (page == 1) { 108 | baseViewBind.statusLayout.empty() 109 | mAdapter.removeAll() 110 | } else { 111 | baseViewBind.statusLayout.snackBar(R.string.data_empty) 112 | } 113 | } 114 | 115 | private fun onXBind(holder: XViewHolder, summaryArrayBean: OpaListBean) { 116 | holder.setText(R.id.tv_project_name, TextUtils.concat("项目名称:", summaryArrayBean.title)) 117 | holder.setText(R.id.tv_summary, summaryArrayBean.summary) 118 | 119 | val projectUrl = holder.findById(R.id.tv_project_url) 120 | projectUrl.autoLinkMask = if (opaUriWebBoolean()) Linkify.WEB_URLS else 0 121 | projectUrl.visibility = if (TextUtils.isEmpty(summaryArrayBean.projectUrl)) View.GONE else View.VISIBLE 122 | projectUrl.text = summaryArrayBean.projectUrl 123 | 124 | val flexboxLayout = holder.findById(R.id.fl_box) 125 | if (opaTagBoolean()) { 126 | flexboxLayout.visibility = View.VISIBLE 127 | flexboxLayout.tags(summaryArrayBean.tagList) { 128 | startActivity(OpSearchActivity.TEXT_KEY to it) 129 | } 130 | } else { 131 | flexboxLayout.visibility = View.GONE 132 | } 133 | } 134 | 135 | companion object { 136 | private const val TEXT_KEY = "text" 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/activity/ReadmeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.activity 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.view.KeyEvent 6 | import android.view.Menu 7 | import android.view.MenuItem 8 | import com.codekk.Constant 9 | import com.codekk.R 10 | import com.codekk.databinding.ActivityReadmeBinding 11 | import com.codekk.ext.ReadmeModel 12 | import com.codekk.ext.empty 13 | import com.codekk.ext.error 14 | import com.codekk.ext.snackBar 15 | import com.codekk.mvp.presenter.impl.ReadmePresenterImpl 16 | import com.codekk.mvp.view.ReadmeView 17 | import com.codekk.ui.base.BaseViewBindActivity 18 | import org.jetbrains.anko.browse 19 | 20 | /** 21 | * by y on 2017/5/16 22 | */ 23 | class ReadmeActivity : BaseViewBindActivity(), ReadmeView { 24 | 25 | companion object { 26 | const val KEY = "key" 27 | const val TYPE = "type" 28 | } 29 | 30 | private val detail by lazy { bundle?.getStringArray(KEY).orEmpty() } 31 | private val type by lazy { bundle?.getInt(TYPE) ?: 0 } 32 | 33 | override fun onDestroy() { 34 | super.onDestroy() 35 | viewBind.markdownView.destroy() 36 | } 37 | 38 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 39 | menuInflater.inflate(R.menu.readme_menu, menu) 40 | if (type == Constant.TYPE_JOB || type == Constant.TYPE_BLOG || type == Constant.TYPE_RECOMMEND) { 41 | menu.findItem(R.id.open_browser).isVisible = false 42 | } 43 | return super.onCreateOptionsMenu(menu) 44 | } 45 | 46 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 47 | return when (item.itemId) { 48 | R.id.refresh_url -> { 49 | if (viewBind.markdownView.isLoading) { 50 | viewBind.markdownView.reload() 51 | } else { 52 | baseViewBind.statusLayout.snackBar(R.string.net_loading) 53 | } 54 | true 55 | } 56 | R.id.open_browser -> { 57 | browse(detail[2], true) 58 | true 59 | } 60 | else -> super.onOptionsItemSelected(item) 61 | } 62 | } 63 | 64 | override fun initViewBind(): ActivityReadmeBinding { 65 | return ActivityReadmeBinding.inflate(layoutInflater) 66 | } 67 | 68 | override fun initPresenter(): ReadmePresenterImpl { 69 | return ReadmePresenterImpl(this) 70 | } 71 | 72 | override fun initCreate(savedInstanceState: Bundle?) { 73 | viewBind.refreshLayout.isEnabled = false 74 | mPresenter.netWorkRequest(detail[0], type) 75 | baseViewBind.toolbarRoot.toolbar.title = detail[1] 76 | setSupportActionBar(baseViewBind.toolbarRoot.toolbar) 77 | } 78 | 79 | override fun clickNetWork() { 80 | super.clickNetWork() 81 | if (!viewBind.refreshLayout.isRefreshing) { 82 | mPresenter.netWorkRequest(detail[0], type) 83 | } 84 | } 85 | 86 | override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { 87 | if (event.action == KeyEvent.ACTION_DOWN) { 88 | if (keyCode == KeyEvent.KEYCODE_BACK && viewBind.markdownView.canGoBack()) { 89 | viewBind.markdownView.goBack() 90 | return true 91 | } 92 | } 93 | return super.onKeyDown(keyCode, event) 94 | } 95 | 96 | override fun showProgress() { 97 | viewBind.refreshLayout.isRefreshing = true 98 | } 99 | 100 | override fun hideProgress() { 101 | viewBind.refreshLayout.isRefreshing = false 102 | } 103 | 104 | override fun netWorkSuccess(entity: ReadmeModel) { 105 | if (!TextUtils.isEmpty(entity.content)) { 106 | viewBind.markdownView.setMarkDownText(entity.content) 107 | } else { 108 | baseViewBind.statusLayout.empty() 109 | } 110 | } 111 | 112 | override fun netWorkError(throwable: Throwable) { 113 | baseViewBind.statusLayout.error() 114 | baseViewBind.statusLayout.snackBar(R.string.net_error) 115 | } 116 | 117 | override fun loadWebViewUrl() { 118 | viewBind.markdownView.loadUrl(detail[2]) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/activity/RecommendSearchActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.activity 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.view.View 6 | import androidx.appcompat.widget.AppCompatTextView 7 | import androidx.core.text.parseAsHtml 8 | import androidx.recyclerview.widget.LinearLayoutManager 9 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 10 | import com.codekk.R 11 | import com.codekk.databinding.LayoutListBinding 12 | import com.codekk.ext.* 13 | import com.codekk.mvp.presenter.impl.RecommendPresenterImpl 14 | import com.codekk.mvp.view.RecommendListView 15 | import com.codekk.ui.base.BaseViewBindActivity 16 | import com.codekk.ui.widget.LoadMoreRecyclerView 17 | import com.xadapter.* 18 | import com.xadapter.adapter.XAdapter 19 | import com.xadapter.holder.XViewHolder 20 | import org.jetbrains.anko.browse 21 | 22 | /** 23 | * by y on 2017/5/20. 24 | */ 25 | class RecommendSearchActivity : BaseViewBindActivity(), RecommendListView, LoadMoreRecyclerView.LoadMoreListener, SwipeRefreshLayout.OnRefreshListener { 26 | 27 | companion object { 28 | const val TEXT_KEY = "text" 29 | } 30 | 31 | private val text by lazy { bundle?.getString(TEXT_KEY, "").orEmpty() } 32 | private val mAdapter by lazy { XAdapter() } 33 | private var page = 1 34 | 35 | override fun initViewBind(): LayoutListBinding { 36 | return LayoutListBinding.inflate(layoutInflater) 37 | } 38 | 39 | override fun initPresenter(): RecommendPresenterImpl { 40 | return RecommendPresenterImpl(this) 41 | } 42 | 43 | override fun initCreate(savedInstanceState: Bundle?) { 44 | baseViewBind.toolbarRoot.toolbar.title = text 45 | setSupportActionBar(baseViewBind.toolbarRoot.toolbar) 46 | 47 | viewBind.recyclerView.setHasFixedSize(true) 48 | viewBind.recyclerView.layoutManager = LinearLayoutManager(this) 49 | 50 | viewBind.recyclerView.adapter = mAdapter 51 | .setItemLayoutId(R.layout.item_recommend_list) 52 | .setOnItemClickListener { _, _, info -> browse(info.url, true) } 53 | .setOnBind { holder, _, entity -> 54 | onXBind(holder, entity) 55 | } 56 | 57 | viewBind.refreshLayout.setOnRefreshListener(this) 58 | viewBind.refreshLayout.post { this.onRefresh() } 59 | } 60 | 61 | override fun clickNetWork() { 62 | super.clickNetWork() 63 | if (!viewBind.refreshLayout.isRefreshing) { 64 | onRefresh() 65 | } 66 | } 67 | 68 | override fun showProgress() { 69 | viewBind.refreshLayout.isRefreshing = true 70 | } 71 | 72 | override fun hideProgress() { 73 | viewBind.refreshLayout.isRefreshing = false 74 | } 75 | 76 | override fun onRefresh() { 77 | baseViewBind.statusLayout.success() 78 | mPresenter.netWorkRequestSearch(text, page = 1) 79 | } 80 | 81 | override fun onLoadMore() { 82 | if (viewBind.refreshLayout.isRefreshing) { 83 | return 84 | } 85 | mPresenter.netWorkRequestSearch(text, page) 86 | } 87 | 88 | 89 | override fun netWorkSuccess(entity: RecommendListModel) { 90 | if (page == 1) { 91 | mAdapter.removeAll() 92 | } 93 | ++page 94 | mAdapter.addAll(entity.recommendList) 95 | } 96 | 97 | override fun netWorkError(throwable: Throwable) { 98 | if (page == 1) { 99 | baseViewBind.statusLayout.error() 100 | mAdapter.removeAll() 101 | } else { 102 | baseViewBind.statusLayout.snackBar(R.string.net_error) 103 | } 104 | } 105 | 106 | override fun noMore() { 107 | if (page == 1) { 108 | baseViewBind.statusLayout.empty() 109 | mAdapter.removeAll() 110 | } else { 111 | baseViewBind.statusLayout.snackBar(R.string.data_empty) 112 | } 113 | } 114 | 115 | private fun onXBind(holder: XViewHolder, recommendArrayBean: RecommendListBean) { 116 | if (!TextUtils.isEmpty(recommendArrayBean.title)) { 117 | holder.setText(R.id.tv_recommend_title, recommendArrayBean.title) 118 | } 119 | val descView = holder.findById(R.id.tv_recommend_desc) 120 | descView.visibility = if (TextUtils.isEmpty(recommendArrayBean.desc)) View.GONE else View.VISIBLE 121 | if (!TextUtils.isEmpty(recommendArrayBean.desc)) { 122 | descView.text = recommendArrayBean.desc.parseAsHtml() 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/activity/SettingActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.activity 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import androidx.appcompat.widget.AppCompatCheckBox 6 | import androidx.recyclerview.widget.LinearLayoutManager 7 | import com.codekk.R 8 | import com.codekk.databinding.ActivitySettingBinding 9 | import com.codekk.ext.* 10 | import com.codekk.ui.base.ViewBindActivity 11 | import com.xadapter.* 12 | import com.xadapter.adapter.XMultiAdapter 13 | 14 | /** 15 | * by y on 2017/5/18 16 | */ 17 | class SettingActivity : ViewBindActivity() { 18 | 19 | override fun initViewBind(): ActivitySettingBinding { 20 | return ActivitySettingBinding.inflate(layoutInflater) 21 | } 22 | 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | viewBind.toolbarRoot.toolbar.setTitle(R.string.setting_title) 26 | setSupportActionBar(viewBind.toolbarRoot.toolbar) 27 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 28 | 29 | viewBind.recyclerView.setHasFixedSize(true) 30 | viewBind.recyclerView.layoutManager = LinearLayoutManager(applicationContext) 31 | 32 | viewBind.recyclerView 33 | .attachMultiAdapter(XMultiAdapter(initItem())) 34 | .setItemLayoutId { viewType -> 35 | when (viewType) { 36 | ExtSetting.TYPE_TITLE -> R.layout.item_setting_title 37 | else -> R.layout.item_setting 38 | } 39 | } 40 | .setMultiBind { holder, entity, itemViewType, _ -> 41 | when (itemViewType) { 42 | ExtSetting.TYPE_TITLE -> holder.setText(R.id.tv_setting_title, entity.message) 43 | ExtSetting.TYPE_ITEM -> { 44 | holder.setText(R.id.tv_setting_message, entity.message) 45 | val tagCheckBox = holder.findById(R.id.cb_setting) 46 | when (entity.itemMultiPosition) { 47 | 0 -> tagCheckBox.isChecked = holder.getContext().opTagBoolean() 48 | 1 -> tagCheckBox.isChecked = holder.getContext().opUriWebBoolean() 49 | 2 -> tagCheckBox.isChecked = holder.getContext().opaTagBoolean() 50 | 3 -> tagCheckBox.isChecked = holder.getContext().opaUriWebBoolean() 51 | 4 -> tagCheckBox.isChecked = holder.getContext().blogTagBoolean() 52 | } 53 | holder.itemView.setOnClickListener { 54 | it.settingItemClick(entity.itemMultiPosition, tagCheckBox.isChecked) 55 | tagCheckBox.isChecked = !tagCheckBox.isChecked 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 63 | if (item.itemId == android.R.id.home) { 64 | finish() 65 | } 66 | return super.onOptionsItemSelected(item) 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.view.MenuItem 6 | import android.view.View 7 | import androidx.appcompat.app.AppCompatActivity 8 | import com.android.status.layout.OnEmptyClick 9 | import com.android.status.layout.OnErrorClick 10 | import com.android.status.layout.addSuccessView 11 | import com.codekk.databinding.ActivityBaseBinding 12 | 13 | /** 14 | * by y on 2017/5/16 15 | */ 16 | abstract class BaseActivity

>(val layout: Int) : AppCompatActivity() { 17 | 18 | protected val bundle: Bundle? by lazy { intent.extras } 19 | 20 | protected val mPresenter by lazy { initPresenter() } 21 | 22 | protected val baseViewBind: ActivityBaseBinding by lazy { ActivityBaseBinding.inflate(layoutInflater) } 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setContentView(baseViewBind.root) 27 | if (layout != View.NO_ID) { 28 | baseViewBind.statusLayout.addSuccessView(layout) 29 | } else { 30 | viewBindCreate(savedInstanceState) 31 | } 32 | baseViewBind.statusLayout 33 | .OnEmptyClick { clickNetWork() } 34 | .OnErrorClick { clickNetWork() } 35 | bundle?.let { initBundle(it) } 36 | initCreate(savedInstanceState) 37 | if (!TextUtils.equals(javaClass.simpleName, "MainActivity")) { 38 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 39 | } 40 | } 41 | 42 | protected abstract fun initPresenter(): P 43 | 44 | protected abstract fun initCreate(savedInstanceState: Bundle?) 45 | 46 | protected open fun initBundle(bundle: Bundle) { 47 | } 48 | 49 | protected open fun viewBindCreate(savedInstanceState: Bundle?) { 50 | } 51 | 52 | protected open fun clickNetWork() {} 53 | 54 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 55 | if (item.itemId == android.R.id.home) { 56 | finish() 57 | } 58 | return super.onOptionsItemSelected(item) 59 | } 60 | 61 | override fun onDestroy() { 62 | super.onDestroy() 63 | mPresenter.onDestroy() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import com.android.status.layout.* 9 | import com.codekk.R 10 | import com.codekk.ext.success 11 | import io.reactivex.network.RxBus 12 | import io.reactivex.network.RxBusCallBack 13 | 14 | /** 15 | * by y on 2017/5/16 16 | */ 17 | abstract class BaseFragment

>(val layout: Int) : Fragment(), RxBusCallBack { 18 | 19 | protected lateinit var mStatusView: StatusLayout 20 | protected val mPresenter by lazy { initPresenter() } 21 | protected var page: Int = 0 22 | 23 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 24 | if (!::mStatusView.isInitialized) { 25 | mStatusView = StatusLayout(inflater.context) 26 | mStatusView.addEmptyView(R.layout.layout_status_empty) 27 | mStatusView.addErrorView(R.layout.layout_status_error) 28 | if (layout != View.NO_ID) { 29 | mStatusView.addSuccessView(layout) 30 | } else { 31 | viewBindCreate(savedInstanceState) 32 | } 33 | mStatusView.success() 34 | mStatusView 35 | .OnEmptyClick { clickNetWork() } 36 | .OnErrorClick { clickNetWork() } 37 | } 38 | RxBus.instance.register(javaClass.simpleName, this) 39 | return mStatusView 40 | } 41 | 42 | override fun onActivityCreated(savedInstanceState: Bundle?) { 43 | super.onActivityCreated(savedInstanceState) 44 | initActivityCreated() 45 | } 46 | 47 | protected open fun viewBindCreate(savedInstanceState: Bundle?) { 48 | } 49 | 50 | protected abstract fun initActivityCreated() 51 | 52 | protected abstract fun initPresenter(): P 53 | 54 | protected open fun clickNetWork() {} 55 | 56 | override fun busOfType(): Class { 57 | return Any::class.java 58 | } 59 | 60 | override fun onDestroyView() { 61 | super.onDestroyView() 62 | RxBus.instance.unregister(javaClass.simpleName) 63 | mPresenter.onDestroy() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BaseListView.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | interface BaseListView : BaseView { 4 | fun noMore() 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BasePresenterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | import io.reactivex.network.RxNetWorkListener 4 | 5 | /** 6 | * by y on 2017/5/16 7 | */ 8 | abstract class BasePresenterImpl, M>(var view: V?) : RxNetWorkListener { 9 | 10 | override fun onNetWorkStart() { 11 | view?.showProgress() 12 | } 13 | 14 | override fun onNetWorkError(e: Throwable) { 15 | view?.hideProgress() 16 | view?.netWorkError(e) 17 | } 18 | 19 | override fun onNetWorkComplete() { 20 | view?.hideProgress() 21 | } 22 | 23 | override fun onNetWorkSuccess(data: M) { 24 | view?.netWorkSuccess(data) 25 | } 26 | 27 | open fun onDestroy() { 28 | if (view != null) 29 | view = null 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BaseView.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | /** 4 | * by y on 2017/5/16 5 | */ 6 | interface BaseView { 7 | 8 | fun showProgress() 9 | 10 | fun hideProgress() 11 | 12 | fun netWorkSuccess(entity: T) 13 | 14 | fun netWorkError(throwable: Throwable) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BaseViewBindActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.viewbinding.ViewBinding 6 | 7 | abstract class BaseViewBindActivity> : BaseActivity

(View.NO_ID) { 8 | 9 | protected val viewBind by lazy { initViewBind() } 10 | 11 | override fun viewBindCreate(savedInstanceState: Bundle?) { 12 | baseViewBind.statusLayout.addView(viewBind.root) 13 | } 14 | 15 | protected abstract fun initViewBind(): BIND 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/BaseViewBindFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.viewbinding.ViewBinding 6 | import com.android.status.layout.addSuccessView 7 | 8 | abstract class BaseViewBindFragment> : BaseFragment

(View.NO_ID) { 9 | 10 | protected val viewBind by lazy { initViewBind() } 11 | 12 | override fun viewBindCreate(savedInstanceState: Bundle?) { 13 | super.viewBindCreate(savedInstanceState) 14 | mStatusView.addSuccessView(viewBind.root) 15 | } 16 | 17 | protected abstract fun initViewBind(): BIND 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/base/ViewBindActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.base 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.viewbinding.ViewBinding 6 | 7 | abstract class ViewBindActivity : AppCompatActivity() { 8 | 9 | protected val viewBind by lazy { initViewBind() } 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(viewBind.root) 14 | } 15 | 16 | protected abstract fun initViewBind(): BIND 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/fragment/BlogListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.fragment 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 6 | import com.codekk.Constant 7 | import com.codekk.R 8 | import com.codekk.databinding.LayoutListBinding 9 | import com.codekk.ext.* 10 | import com.codekk.mvp.presenter.impl.BlogListPresenterImpl 11 | import com.codekk.mvp.view.BlogListView 12 | import com.codekk.ui.activity.OpSearchActivity 13 | import com.codekk.ui.activity.ReadmeActivity 14 | import com.codekk.ui.base.BaseViewBindFragment 15 | import com.codekk.ui.widget.LoadMoreRecyclerView 16 | import com.google.android.flexbox.FlexboxLayout 17 | import com.xadapter.* 18 | import com.xadapter.adapter.XAdapter 19 | import com.xadapter.holder.XViewHolder 20 | import org.jetbrains.anko.support.v4.startActivity 21 | 22 | /** 23 | * by y on 2017/5/19 24 | */ 25 | class BlogListFragment : BaseViewBindFragment(), SwipeRefreshLayout.OnRefreshListener, BlogListView, LoadMoreRecyclerView.LoadMoreListener { 26 | 27 | private val mAdapter by lazy { XAdapter() } 28 | 29 | override fun initViewBind(): LayoutListBinding { 30 | return LayoutListBinding.inflate(layoutInflater) 31 | } 32 | 33 | override fun initPresenter(): BlogListPresenterImpl { 34 | return BlogListPresenterImpl(this) 35 | } 36 | 37 | override fun initActivityCreated() { 38 | viewBind.recyclerView.setHasFixedSize(true) 39 | viewBind.recyclerView.layoutManager = LinearLayoutManager(activity) 40 | viewBind.recyclerView.setLoadingListener(this) //一页显示,不用分页 41 | 42 | viewBind.recyclerView.adapter = mAdapter 43 | .setItemLayoutId(R.layout.item_blog_list) 44 | .setOnItemClickListener { _, _, info -> 45 | startActivity( 46 | ReadmeActivity.KEY to arrayOf(info.id, info.authorName), 47 | ReadmeActivity.TYPE to Constant.TYPE_BLOG 48 | ) 49 | } 50 | .setOnBind { holder, _, entity -> 51 | onXBind(holder, entity) 52 | } 53 | 54 | viewBind.refreshLayout.setOnRefreshListener(this) 55 | viewBind.refreshLayout.post { this.onRefresh() } 56 | } 57 | 58 | override fun clickNetWork() { 59 | super.clickNetWork() 60 | if (!viewBind.refreshLayout.isRefreshing) { 61 | onRefresh() 62 | } 63 | } 64 | 65 | override fun onRefresh() { 66 | page = 1 67 | mStatusView.success() 68 | mPresenter.netWorkRequest(page) 69 | } 70 | 71 | override fun onLoadMore() { 72 | if (viewBind.refreshLayout.isRefreshing) { 73 | return 74 | } 75 | mPresenter.netWorkRequest(page) 76 | } 77 | 78 | override fun showProgress() { 79 | viewBind.refreshLayout.isRefreshing = true 80 | } 81 | 82 | override fun hideProgress() { 83 | viewBind.refreshLayout.isRefreshing = false 84 | } 85 | 86 | override fun netWorkSuccess(entity: BlogListModel) { 87 | if (page == 1) { 88 | mAdapter.removeAll() 89 | } 90 | page += 1 91 | mAdapter.addAll(entity.blogList) 92 | } 93 | 94 | override fun netWorkError(throwable: Throwable) { 95 | if (page == 1) { 96 | mStatusView.error() 97 | mAdapter.removeAll() 98 | } else { 99 | mStatusView.snackBar(R.string.net_error) 100 | } 101 | } 102 | 103 | override fun noMore() { 104 | if (page == 1) { 105 | mStatusView.empty() 106 | mAdapter.removeAll() 107 | } else { 108 | mStatusView.snackBar(R.string.data_empty) 109 | } 110 | } 111 | 112 | private fun onXBind(holder: XViewHolder, summaryArrayBean: BlogListBean) { 113 | holder.setText(R.id.tv_blog_title, summaryArrayBean.title) 114 | holder.setText(R.id.tv_blog_summary, summaryArrayBean.summary) 115 | val flexboxLayout = holder.findById(R.id.fl_box) 116 | if (holder.getContext().blogTagBoolean()) { 117 | flexboxLayout.visibility = View.VISIBLE 118 | flexboxLayout.tags(summaryArrayBean.tagList) { 119 | startActivity(OpSearchActivity.TEXT_KEY to it) 120 | } 121 | } else { 122 | flexboxLayout.visibility = View.GONE 123 | } 124 | } 125 | 126 | override fun onBusNext(entity: Any) { 127 | mAdapter.notifyDataSetChanged() 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/fragment/JobListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.fragment 2 | 3 | import android.text.TextUtils 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 6 | import com.codekk.Constant 7 | import com.codekk.R 8 | import com.codekk.databinding.LayoutListBinding 9 | import com.codekk.ext.* 10 | import com.codekk.mvp.presenter.impl.JobListPresenterImpl 11 | import com.codekk.mvp.view.JobListView 12 | import com.codekk.ui.activity.ReadmeActivity 13 | import com.codekk.ui.base.BaseViewBindFragment 14 | import com.codekk.ui.widget.LoadMoreRecyclerView 15 | import com.xadapter.* 16 | import com.xadapter.adapter.XAdapter 17 | import com.xadapter.holder.XViewHolder 18 | import org.jetbrains.anko.support.v4.startActivity 19 | 20 | /** 21 | * by y on 2017/5/18. 22 | */ 23 | class JobListFragment : BaseViewBindFragment(), SwipeRefreshLayout.OnRefreshListener, JobListView, LoadMoreRecyclerView.LoadMoreListener { 24 | 25 | private val mAdapter by lazy { XAdapter() } 26 | 27 | override fun initViewBind(): LayoutListBinding { 28 | return LayoutListBinding.inflate(layoutInflater) 29 | } 30 | 31 | override fun initPresenter(): JobListPresenterImpl { 32 | return JobListPresenterImpl(this) 33 | } 34 | 35 | override fun initActivityCreated() { 36 | viewBind.recyclerView.setHasFixedSize(true) 37 | viewBind.recyclerView.layoutManager = LinearLayoutManager(activity) 38 | viewBind.recyclerView.setLoadingListener(this) 39 | 40 | viewBind.recyclerView.adapter = mAdapter 41 | .setItemLayoutId(R.layout.item_job_list) 42 | .setOnItemClickListener { _, _, info -> 43 | startActivity( 44 | ReadmeActivity.KEY to arrayOf(info.id, info.authorName), 45 | ReadmeActivity.TYPE to Constant.TYPE_JOB 46 | ) 47 | } 48 | .setOnBind { holder, _, entity -> onXBind(holder, entity) } 49 | 50 | viewBind.refreshLayout.setOnRefreshListener(this) 51 | viewBind.refreshLayout.post { this.onRefresh() } 52 | } 53 | 54 | override fun clickNetWork() { 55 | super.clickNetWork() 56 | if (!viewBind.refreshLayout.isRefreshing) { 57 | onRefresh() 58 | } 59 | } 60 | 61 | override fun onRefresh() { 62 | mStatusView.success() 63 | page = 1 64 | mPresenter.netWorkRequest(page) 65 | } 66 | 67 | override fun onLoadMore() { 68 | if (viewBind.refreshLayout.isRefreshing) { 69 | return 70 | } 71 | mPresenter.netWorkRequest(page) 72 | } 73 | 74 | override fun showProgress() { 75 | viewBind.refreshLayout.isRefreshing = true 76 | } 77 | 78 | override fun hideProgress() { 79 | viewBind.refreshLayout.isRefreshing = false 80 | } 81 | 82 | override fun netWorkSuccess(entity: JobListModel) { 83 | if (page == 1) { 84 | mAdapter.removeAll() 85 | } 86 | page += 1 87 | mAdapter.addAll(entity.jobList) 88 | } 89 | 90 | override fun netWorkError(throwable: Throwable) { 91 | if (page == 1) { 92 | mStatusView.error() 93 | mAdapter.removeAll() 94 | } else { 95 | mStatusView.snackBar(R.string.net_error) 96 | } 97 | } 98 | 99 | override fun noMore() { 100 | if (page == 1) { 101 | mStatusView.empty() 102 | mAdapter.removeAll() 103 | } else { 104 | mStatusView.snackBar(R.string.data_empty) 105 | } 106 | } 107 | 108 | private fun onXBind(holder: XViewHolder, summaryArrayBean: JobListBean) { 109 | holder.setText(R.id.tv_job_title, TextUtils.concat(summaryArrayBean.authorName)) 110 | holder.setText(R.id.tv_job_address, TextUtils.concat("地点:", summaryArrayBean.authorCity)) 111 | holder.setText(R.id.tv_job_expiredTime, TextUtils.concat("截止时间:", summaryArrayBean.expiredTime)) 112 | holder.setText(R.id.tv_job_summary, TextUtils.concat(summaryArrayBean.summary)) 113 | } 114 | 115 | override fun onBusNext(entity: Any) { 116 | mAdapter.notifyDataSetChanged() 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/fragment/OpListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.fragment 2 | 3 | import android.text.TextUtils 4 | import android.text.util.Linkify 5 | import android.view.Menu 6 | import android.view.MenuInflater 7 | import android.view.MenuItem 8 | import android.view.View 9 | import androidx.appcompat.widget.AppCompatTextView 10 | import androidx.core.os.bundleOf 11 | import androidx.core.text.parseAsHtml 12 | import androidx.recyclerview.widget.LinearLayoutManager 13 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 14 | import com.codekk.Constant 15 | import com.codekk.R 16 | import com.codekk.databinding.LayoutListBinding 17 | import com.codekk.ext.* 18 | import com.codekk.mvp.presenter.impl.OpPresenterImpl 19 | import com.codekk.mvp.view.OpListView 20 | import com.codekk.ui.activity.OpSearchActivity 21 | import com.codekk.ui.activity.ReadmeActivity 22 | import com.codekk.ui.base.BaseViewBindFragment 23 | import com.codekk.ui.widget.LoadMoreRecyclerView 24 | import com.google.android.flexbox.FlexboxLayout 25 | import com.xadapter.* 26 | import com.xadapter.adapter.XAdapter 27 | import com.xadapter.holder.XViewHolder 28 | import org.jetbrains.anko.support.v4.startActivity 29 | 30 | /** 31 | * by y on 2017/5/16 32 | */ 33 | class OpListFragment : BaseViewBindFragment(), SwipeRefreshLayout.OnRefreshListener, OpListView, LoadMoreRecyclerView.LoadMoreListener { 34 | 35 | companion object { 36 | fun get(text: String): OpListFragment { 37 | return OpListFragment().apply { 38 | arguments = bundleOf( 39 | OpSearchActivity.TEXT_KEY to text 40 | ) 41 | } 42 | } 43 | } 44 | 45 | private val mAdapter by lazy { XAdapter() } 46 | private val searchText by lazy { arguments?.getString(OpSearchActivity.TEXT_KEY, "").orEmpty() } 47 | 48 | override fun initViewBind(): LayoutListBinding { 49 | return LayoutListBinding.inflate(layoutInflater) 50 | } 51 | 52 | override fun initPresenter(): OpPresenterImpl = OpPresenterImpl(this) 53 | 54 | override fun initActivityCreated() { 55 | setHasOptionsMenu(true) 56 | viewBind.recyclerView.setHasFixedSize(true) 57 | viewBind.recyclerView.layoutManager = LinearLayoutManager(activity) 58 | viewBind.recyclerView.setLoadingListener(this) 59 | 60 | viewBind.recyclerView.adapter = mAdapter 61 | .setItemLayoutId(R.layout.item_op_list) 62 | .setOnItemClickListener { _, _, info -> 63 | startActivity(ReadmeActivity.KEY to arrayOf(info.id, info.projectName, info.projectUrl), ReadmeActivity.TYPE to Constant.TYPE_OP) 64 | } 65 | .setOnBind { holder, _, entity -> onXBind(holder, entity) } 66 | 67 | viewBind.refreshLayout.setOnRefreshListener(this) 68 | viewBind.refreshLayout.post { this.onRefresh() } 69 | activity?.findViewById(R.id.toolbar)?.setOnClickListener { viewBind.recyclerView.smoothScrollToPosition(0) } 70 | } 71 | 72 | override fun clickNetWork() { 73 | super.clickNetWork() 74 | if (!viewBind.refreshLayout.isRefreshing) { 75 | onRefresh() 76 | } 77 | } 78 | 79 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 80 | if (searchText.isEmpty()) { 81 | inflater.inflate(R.menu.search_menu, menu) 82 | } 83 | super.onCreateOptionsMenu(menu, inflater) 84 | } 85 | 86 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 87 | activity?.let { 88 | return when (item.itemId) { 89 | R.id.open_search -> { 90 | it.openSearch(R.string.search_op_hint) { s -> startActivity(OpSearchActivity.TEXT_KEY to s) } 91 | return true 92 | } 93 | else -> super.onOptionsItemSelected(item) 94 | } 95 | } ?: return super.onOptionsItemSelected(item) 96 | } 97 | 98 | override fun onRefresh() { 99 | mStatusView.success() 100 | page = 1 101 | if (searchText.isEmpty()) { 102 | mPresenter.netWorkRequestList(page) 103 | } else { 104 | mPresenter.netWorkRequestSearch(searchText, page) 105 | } 106 | } 107 | 108 | override fun onLoadMore() { 109 | if (viewBind.refreshLayout.isRefreshing) { 110 | return 111 | } 112 | if (searchText.isEmpty()) { 113 | mPresenter.netWorkRequestList(page) 114 | } else { 115 | mPresenter.netWorkRequestSearch(searchText, page) 116 | } 117 | } 118 | 119 | override fun showProgress() { 120 | viewBind.refreshLayout.isRefreshing = true 121 | } 122 | 123 | override fun hideProgress() { 124 | viewBind.refreshLayout.isRefreshing = false 125 | } 126 | 127 | override fun netWorkSuccess(entity: OpListModel) { 128 | if (page == 1) { 129 | mAdapter.removeAll() 130 | } 131 | page += 1 132 | mAdapter.addAll(entity.opList) 133 | } 134 | 135 | 136 | override fun netWorkError(throwable: Throwable) { 137 | if (page == 1) { 138 | mStatusView.error() 139 | mAdapter.removeAll() 140 | } else { 141 | mStatusView.snackBar(R.string.net_error) 142 | } 143 | } 144 | 145 | override fun noMore() { 146 | if (page == 1) { 147 | mStatusView.empty() 148 | mAdapter.removeAll() 149 | } else { 150 | mStatusView.snackBar(R.string.data_empty) 151 | } 152 | } 153 | 154 | private fun onXBind(holder: XViewHolder, projectArrayBean: OpListBean) { 155 | holder.setText(R.id.tv_author_name, TextUtils.concat("添加者:", projectArrayBean.authorName)) 156 | holder.setText(R.id.tv_author_url, TextUtils.concat("个人主页:", projectArrayBean.authorUrl)) 157 | holder.setText(R.id.tv_project_name, TextUtils.concat("项目名称:", projectArrayBean.projectName)) 158 | 159 | val projectUrl = holder.findById(R.id.tv_project_url) 160 | projectUrl.autoLinkMask = if (holder.getContext().opUriWebBoolean()) Linkify.WEB_URLS else 0 161 | projectUrl.text = projectArrayBean.projectUrl 162 | 163 | val textView = holder.findById(R.id.tv_desc) 164 | val descEmpty = TextUtils.isEmpty(projectArrayBean.desc) 165 | textView.visibility = if (descEmpty) View.GONE else View.VISIBLE 166 | textView.text = if (descEmpty) "" else projectArrayBean.desc.parseAsHtml() 167 | val tagLayout = holder.findById(R.id.fl_box) 168 | if (holder.getContext().opTagBoolean()) { 169 | tagLayout.visibility = View.VISIBLE 170 | tagLayout.tags(projectArrayBean.tags()) { startActivity(OpSearchActivity.TEXT_KEY to it) } 171 | } else { 172 | tagLayout.visibility = View.GONE 173 | } 174 | } 175 | 176 | override fun onBusNext(entity: Any) { 177 | mAdapter.notifyDataSetChanged() 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/fragment/OpaListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.fragment 2 | 3 | import android.text.TextUtils 4 | import android.text.util.Linkify 5 | import android.view.Menu 6 | import android.view.MenuInflater 7 | import android.view.MenuItem 8 | import android.view.View 9 | import androidx.appcompat.widget.AppCompatTextView 10 | import androidx.recyclerview.widget.LinearLayoutManager 11 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 12 | import com.codekk.Constant 13 | import com.codekk.R 14 | import com.codekk.databinding.LayoutListBinding 15 | import com.codekk.ext.* 16 | import com.codekk.mvp.presenter.impl.OpaPresenterImpl 17 | import com.codekk.mvp.view.OpaListView 18 | import com.codekk.ui.activity.OpSearchActivity 19 | import com.codekk.ui.activity.ReadmeActivity 20 | import com.codekk.ui.base.BaseViewBindFragment 21 | import com.codekk.ui.widget.LoadMoreRecyclerView 22 | import com.google.android.flexbox.FlexboxLayout 23 | import com.xadapter.* 24 | import com.xadapter.adapter.XAdapter 25 | import com.xadapter.holder.XViewHolder 26 | import org.jetbrains.anko.support.v4.startActivity 27 | 28 | /** 29 | * by y on 2017/5/18 30 | */ 31 | class OpaListFragment : BaseViewBindFragment(), OpaListView, SwipeRefreshLayout.OnRefreshListener, LoadMoreRecyclerView.LoadMoreListener { 32 | 33 | private val mAdapter by lazy { XAdapter() } 34 | 35 | override fun initViewBind(): LayoutListBinding { 36 | return LayoutListBinding.inflate(layoutInflater) 37 | } 38 | 39 | override fun initPresenter(): OpaPresenterImpl { 40 | return OpaPresenterImpl(this) 41 | } 42 | 43 | override fun initActivityCreated() { 44 | setHasOptionsMenu(true) 45 | viewBind.recyclerView.setHasFixedSize(true) 46 | viewBind.recyclerView.layoutManager = LinearLayoutManager(activity) 47 | viewBind.recyclerView.setLoadingListener(this) // 太少了,注掉上拉加载 48 | viewBind.recyclerView.adapter = mAdapter 49 | .setItemLayoutId(R.layout.item_opa_list) 50 | .setOnItemClickListener { _, _, info -> 51 | startActivity( 52 | ReadmeActivity.KEY to arrayOf(info.id, info.projectName, info.projectUrl), 53 | ReadmeActivity.TYPE to Constant.TYPE_OPA 54 | ) 55 | } 56 | .setOnBind { holder, position, entity -> onXBind(holder, entity) } 57 | 58 | viewBind.refreshLayout.setOnRefreshListener(this) 59 | viewBind.refreshLayout.post { this.onRefresh() } 60 | } 61 | 62 | override fun clickNetWork() { 63 | super.clickNetWork() 64 | if (!viewBind.refreshLayout.isRefreshing) { 65 | onRefresh() 66 | } 67 | } 68 | 69 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 70 | inflater.inflate(R.menu.search_menu, menu) 71 | super.onCreateOptionsMenu(menu, inflater) 72 | } 73 | 74 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 75 | activity?.let { 76 | return when (item.itemId) { 77 | R.id.open_search -> { 78 | it.openSearch(R.string.search_opa_hint) { s -> startActivity(OpSearchActivity.TEXT_KEY to s) } 79 | return true 80 | } 81 | else -> super.onOptionsItemSelected(item) 82 | } 83 | } ?: return super.onOptionsItemSelected(item) 84 | } 85 | 86 | override fun onRefresh() { 87 | mStatusView.success() 88 | page = 1 89 | mPresenter.netWorkRequestList(page) 90 | } 91 | 92 | override fun onLoadMore() { 93 | if (viewBind.refreshLayout.isRefreshing) { 94 | return 95 | } 96 | mPresenter.netWorkRequestList(page) 97 | } 98 | 99 | override fun showProgress() { 100 | viewBind.refreshLayout.isRefreshing = true 101 | } 102 | 103 | override fun hideProgress() { 104 | viewBind.refreshLayout.isRefreshing = false 105 | } 106 | 107 | override fun netWorkSuccess(entity: OpaListModel) { 108 | if (page == 1) { 109 | mAdapter.removeAll() 110 | } 111 | page += 1 112 | mAdapter.addAll(entity.opaList) 113 | } 114 | 115 | override fun netWorkError(throwable: Throwable) { 116 | if (page == 1) { 117 | mStatusView.error() 118 | mAdapter.removeAll() 119 | } else { 120 | mStatusView.snackBar(R.string.net_error) 121 | } 122 | } 123 | 124 | override fun noMore() { 125 | if (page == 1) { 126 | mStatusView.empty() 127 | mAdapter.removeAll() 128 | } else { 129 | mStatusView.snackBar(R.string.data_empty) 130 | } 131 | } 132 | 133 | private fun onXBind(holder: XViewHolder, summaryArrayBean: OpaListBean) { 134 | holder.setText(R.id.tv_project_name, TextUtils.concat("项目名称:", summaryArrayBean.title)) 135 | holder.setText(R.id.tv_summary, summaryArrayBean.summary) 136 | 137 | val projectUrl = holder.findById(R.id.tv_project_url) 138 | projectUrl.autoLinkMask = if (holder.getContext().opaUriWebBoolean()) Linkify.WEB_URLS else 0 139 | projectUrl.visibility = if (TextUtils.isEmpty(summaryArrayBean.projectUrl)) View.GONE else View.VISIBLE 140 | projectUrl.text = summaryArrayBean.projectUrl 141 | val flexboxLayout = holder.findById(R.id.fl_box) 142 | if (holder.getContext().opaTagBoolean()) { 143 | flexboxLayout.visibility = View.VISIBLE 144 | flexboxLayout.tags(summaryArrayBean.tagList) { 145 | startActivity(OpSearchActivity.TEXT_KEY to it) 146 | } 147 | } else { 148 | flexboxLayout.visibility = View.GONE 149 | } 150 | } 151 | 152 | override fun onBusNext(entity: Any) { 153 | mAdapter.notifyDataSetChanged() 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/fragment/RecommendListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.fragment 2 | 3 | import android.text.TextUtils 4 | import android.view.Menu 5 | import android.view.MenuInflater 6 | import android.view.MenuItem 7 | import android.view.View 8 | import androidx.appcompat.widget.AppCompatTextView 9 | import androidx.core.text.parseAsHtml 10 | import androidx.recyclerview.widget.LinearLayoutManager 11 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 12 | import com.codekk.Constant 13 | import com.codekk.R 14 | import com.codekk.databinding.LayoutListBinding 15 | import com.codekk.ext.* 16 | import com.codekk.mvp.presenter.impl.RecommendPresenterImpl 17 | import com.codekk.mvp.view.RecommendListView 18 | import com.codekk.ui.activity.ReadmeActivity 19 | import com.codekk.ui.activity.RecommendSearchActivity 20 | import com.codekk.ui.base.BaseViewBindFragment 21 | import com.codekk.ui.widget.LoadMoreRecyclerView 22 | import com.xadapter.* 23 | import com.xadapter.adapter.XAdapter 24 | import com.xadapter.holder.XViewHolder 25 | import org.jetbrains.anko.support.v4.startActivity 26 | 27 | /** 28 | * by y on 2017/5/18 29 | */ 30 | class RecommendListFragment : BaseViewBindFragment(), RecommendListView, SwipeRefreshLayout.OnRefreshListener, LoadMoreRecyclerView.LoadMoreListener { 31 | 32 | private val mAdapter by lazy { XAdapter() } 33 | 34 | override fun initViewBind(): LayoutListBinding { 35 | return LayoutListBinding.inflate(layoutInflater) 36 | } 37 | 38 | override fun initPresenter(): RecommendPresenterImpl { 39 | return RecommendPresenterImpl(this) 40 | } 41 | 42 | override fun initActivityCreated() { 43 | setHasOptionsMenu(true) 44 | viewBind.recyclerView.setHasFixedSize(true) 45 | viewBind.recyclerView.layoutManager = LinearLayoutManager(activity) 46 | viewBind.recyclerView.setLoadingListener(this) 47 | viewBind.recyclerView.adapter = mAdapter 48 | .setItemLayoutId(R.layout.item_recommend_list) 49 | .setOnItemClickListener { _, _, info -> 50 | startActivity( 51 | ReadmeActivity.TYPE to arrayOf("0", info.title, info.url), 52 | ReadmeActivity.KEY to Constant.TYPE_RECOMMEND 53 | ) 54 | } 55 | .setOnBind { holder, position, entity -> onXBind(holder, entity) } 56 | 57 | viewBind.refreshLayout.setOnRefreshListener(this) 58 | viewBind.refreshLayout.post { this.onRefresh() } 59 | 60 | } 61 | 62 | override fun clickNetWork() { 63 | super.clickNetWork() 64 | if (!viewBind.refreshLayout.isRefreshing) { 65 | onRefresh() 66 | } 67 | } 68 | 69 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 70 | inflater.inflate(R.menu.search_menu, menu) 71 | super.onCreateOptionsMenu(menu, inflater) 72 | } 73 | 74 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 75 | activity?.let { 76 | return when (item.itemId) { 77 | R.id.open_search -> { 78 | it.openSearch(R.string.search_recommend_hint) { s -> 79 | startActivity( 80 | RecommendSearchActivity.TEXT_KEY to s 81 | ) 82 | } 83 | return true 84 | } 85 | else -> super.onOptionsItemSelected(item) 86 | } 87 | } ?: return super.onOptionsItemSelected(item) 88 | } 89 | 90 | override fun onRefresh() { 91 | mStatusView.success() 92 | page = 1 93 | mPresenter.netWorkRequestList(page) 94 | } 95 | 96 | override fun onLoadMore() { 97 | if (viewBind.refreshLayout.isRefreshing) { 98 | return 99 | } 100 | mPresenter.netWorkRequestList(page) 101 | } 102 | 103 | override fun showProgress() { 104 | viewBind.refreshLayout.isRefreshing = true 105 | } 106 | 107 | override fun hideProgress() { 108 | viewBind.refreshLayout.isRefreshing = false 109 | } 110 | 111 | override fun netWorkSuccess(entity: RecommendListModel) { 112 | if (page == 1) { 113 | mAdapter.removeAll() 114 | } 115 | page += 1 116 | mAdapter.addAll(entity.recommendList) 117 | } 118 | 119 | override fun netWorkError(throwable: Throwable) { 120 | if (page == 1) { 121 | mStatusView.error() 122 | mAdapter.removeAll() 123 | } else { 124 | mStatusView.snackBar(R.string.net_error) 125 | } 126 | } 127 | 128 | override fun noMore() { 129 | if (page == 1) { 130 | mStatusView.empty() 131 | mAdapter.removeAll() 132 | } else { 133 | mStatusView.snackBar(R.string.data_empty) 134 | } 135 | } 136 | 137 | 138 | private fun onXBind(holder: XViewHolder, recommendArrayBean: RecommendListBean) { 139 | if (!TextUtils.isEmpty(recommendArrayBean.title)) { 140 | holder.setText(R.id.tv_recommend_title, recommendArrayBean.title) 141 | } 142 | val descView = holder.findById(R.id.tv_recommend_desc) 143 | descView.visibility = if (TextUtils.isEmpty(recommendArrayBean.desc)) View.GONE else View.VISIBLE 144 | if (!TextUtils.isEmpty(recommendArrayBean.desc)) { 145 | descView.text = recommendArrayBean.desc.parseAsHtml() 146 | } 147 | } 148 | 149 | override fun onBusNext(entity: Any) { 150 | 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/widget/FlowText.kt: -------------------------------------------------------------------------------- 1 | package com.codekk.ui.widget 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.Gravity 6 | import android.view.ViewGroup 7 | import androidx.appcompat.widget.AppCompatTextView 8 | import androidx.core.content.ContextCompat 9 | import com.codekk.R 10 | import com.google.android.flexbox.FlexboxLayout 11 | 12 | 13 | /** 14 | * by y on 2017/5/18 15 | */ 16 | class FlowText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatTextView(context, attrs, defStyleAttr) { 17 | init { 18 | setPadding(18, 10, 18, 10) 19 | val params = FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) 20 | params.leftMargin = 10 21 | params.bottomMargin = 10 22 | gravity = Gravity.CENTER 23 | textSize = 10f 24 | setTextColor(ContextCompat.getColor(context, R.color.colorWhite)) 25 | layoutParams = params 26 | setBackgroundResource(R.drawable.shape_tag) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/widget/LoadMoreRecyclerView.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ClassName") 2 | 3 | package com.codekk.ui.widget 4 | 5 | import android.content.Context 6 | import android.util.AttributeSet 7 | 8 | import androidx.recyclerview.widget.GridLayoutManager 9 | import androidx.recyclerview.widget.LinearLayoutManager 10 | import androidx.recyclerview.widget.RecyclerView 11 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 12 | 13 | /** 14 | * by y on 2017/5/16 15 | */ 16 | class LoadMoreRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : RecyclerView(context, attrs, defStyleAttr) { 17 | 18 | private var layoutManagerType: LAYOUT_MANAGER_TYPE? = null 19 | private var lastPositions: IntArray? = null 20 | private var lastVisibleItemPosition: Int = 0 21 | private var loadingListener: LoadMoreListener? = null 22 | 23 | fun setLoadingListener(loadingListener: LoadMoreListener) { 24 | this.loadingListener = loadingListener 25 | } 26 | 27 | override fun onScrolled(dx: Int, dy: Int) { 28 | super.onScrolled(dx, dy) 29 | val layoutManager = layoutManager 30 | if (layoutManagerType == null) { 31 | layoutManagerType = when (layoutManager) { 32 | is GridLayoutManager -> LAYOUT_MANAGER_TYPE.GRID 33 | is LinearLayoutManager -> LAYOUT_MANAGER_TYPE.LINEAR 34 | is StaggeredGridLayoutManager -> LAYOUT_MANAGER_TYPE.STAGGERED_GRID 35 | else -> throw RuntimeException( 36 | "Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager") 37 | } 38 | } 39 | when (layoutManagerType) { 40 | LAYOUT_MANAGER_TYPE.LINEAR -> lastVisibleItemPosition = (layoutManager as LinearLayoutManager).findLastVisibleItemPosition() 41 | LAYOUT_MANAGER_TYPE.GRID -> lastVisibleItemPosition = (layoutManager as GridLayoutManager).findLastVisibleItemPosition() 42 | LAYOUT_MANAGER_TYPE.STAGGERED_GRID -> { 43 | val staggeredGridLayoutManager = layoutManager as StaggeredGridLayoutManager 44 | if (lastPositions == null) { 45 | lastPositions = IntArray(staggeredGridLayoutManager.spanCount) 46 | } 47 | staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions) 48 | lastPositions?.let { 49 | lastVisibleItemPosition = findMax(it) 50 | } 51 | } 52 | } 53 | } 54 | 55 | override fun onScrollStateChanged(state: Int) { 56 | super.onScrollStateChanged(state) 57 | val layoutManager = layoutManager 58 | val visibleItemCount = layoutManager?.childCount ?: -1 59 | val totalItemCount = layoutManager?.itemCount ?: -1 60 | if (visibleItemCount > 0 && state == SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 1) { 61 | loadingListener?.onLoadMore() 62 | } 63 | } 64 | 65 | private fun findMax(lastPositions: IntArray): Int { 66 | var max = lastPositions[0] 67 | for (value in lastPositions) { 68 | if (value > max) { 69 | max = value 70 | } 71 | } 72 | return max 73 | } 74 | 75 | private enum class LAYOUT_MANAGER_TYPE { 76 | LINEAR, 77 | GRID, 78 | STAGGERED_GRID 79 | } 80 | 81 | interface LoadMoreListener { 82 | fun onLoadMore() 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codekk/ui/widget/SimpleMarkdownView.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("DEPRECATION") 2 | 3 | package com.codekk.ui.widget 4 | 5 | import android.content.Context 6 | import android.util.AttributeSet 7 | import android.view.View 8 | import android.webkit.WebChromeClient 9 | import android.webkit.WebView 10 | import android.widget.ProgressBar 11 | import com.mukesh.MarkdownView 12 | 13 | /** 14 | * by y on 2017/5/17 15 | */ 16 | class SimpleMarkdownView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : MarkdownView(context, attrs, defStyleAttr) { 17 | 18 | private val progressbar: ProgressBar = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal) 19 | var isLoading = false 20 | private set 21 | 22 | init { 23 | progressbar.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 26, 0, 0) 24 | addView(progressbar) 25 | webChromeClient = object : WebChromeClient() { 26 | override fun onProgressChanged(view: WebView, newProgress: Int) { 27 | super.onProgressChanged(view, newProgress) 28 | newProgressBar(newProgress) 29 | } 30 | } 31 | } 32 | 33 | private fun newProgressBar(newProgress: Int) { 34 | if (newProgress == 100) { 35 | progressbar.visibility = View.GONE 36 | isLoading = true 37 | } else { 38 | if (progressbar.visibility == View.GONE) { 39 | progressbar.visibility = View.VISIBLE 40 | } 41 | isLoading = false 42 | progressbar.progress = newProgress 43 | } 44 | } 45 | 46 | override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { 47 | val lp = progressbar.layoutParams as LayoutParams 48 | lp.x = l 49 | lp.y = t 50 | progressbar.layoutParams = lp 51 | super.onScrollChanged(l, t, oldl, oldt) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_status_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/drawable-xxhdpi/ic_status_empty.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_status_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/drawable-xxhdpi/ic_status_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_autorenew.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_blog.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_browser.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_box.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_box_outline.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_common.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_job.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notes.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_op.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_opa.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_recommend.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/select_setting_box.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_tag.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 16 | 17 | 22 | 23 | 33 | 34 | 35 | 36 | 37 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_readme.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/drawer_header_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_blog_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 | 27 | 28 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_job_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 20 | 21 | 27 | 28 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | 48 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_op_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 28 | 29 | 34 | 35 | 42 | 43 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_opa_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 | 28 | 29 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recommend_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 28 | 29 | 34 | 35 | 42 | 43 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_setting_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_status_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_status_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/menu/drawerlayout_menu.xml: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 9 | 13 | 17 | 21 | 25 | 29 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/menu/readme_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/search_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/setting_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/main_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | #fff 8 | #000 9 | #6633CC 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6dp 4 | 5dp 5 | 10dp 6 | 10dp 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CodeKK 3 | 4 | 开源项目 5 | 源码解析 6 | 职位内推 7 | 博客文章 8 | 个人笔记(暂未开放) 9 | 今日推荐 10 | 公共功能(暂未开放) 11 | 设置 12 | 13 | Browser 14 | Refresh 15 | 16 | search 17 | 请输入关键词 18 | search title,tags,author,keywords… 19 | author name 20 | author name 21 | 22 | 网络连接失败,点击重试 23 | loading… 24 | 没有数据了 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.4.20' 3 | repositories { 4 | maven { url 'https://maven.aliyun.com/repository/google' } 5 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 6 | maven { url 'https://maven.aliyun.com/repository/public/' } 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.1' 10 | classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | allprojects { 15 | repositories { 16 | maven { url 'https://maven.aliyun.com/repository/google' } 17 | maven { url 'https://maven.aliyun.com/repository/public/' } 18 | maven { url "https://jitpack.io" } 19 | } 20 | } 21 | ext { 22 | applicationId = 'com.codekk' 23 | compileSdkVersion = 29 24 | minSdkVersion = 19 25 | targetSdkVersion = 29 26 | versionCode = 1 27 | versionName = '1.0' 28 | 29 | appcompatVersion = '1.1.0' 30 | materialVersion = '1.0.0' 31 | ktxVersion = '1.1.0' 32 | ankoVersion = '0.10.8' 33 | logVersion = '3.5.0' 34 | legacysupportv4Version = '1.0.0' 35 | constranintlayoutVersion = '1.1.3' 36 | navigationVersion = '2.1.0' 37 | lifecycleExtensionsVersion = '2.1.0' 38 | 39 | rxNetWorkVersion = '0.2.0' 40 | rxandroidVersion = '2.1.1' 41 | retrofitVersion = '2.6.2' 42 | flexboxVersion = '1.1.1' 43 | materialDialogVersion = '3.1.1' 44 | markdownViewVersion = '1.0.8' 45 | 46 | xAdapterVersion = '0.0.9.8.4' 47 | statusVersion = 'beta09' 48 | } 49 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Dec 02 13:31:29 CST 2020 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-6.5-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /screenshots/blog_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/screenshots/blog_image.gif -------------------------------------------------------------------------------- /screenshots/codekk_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/screenshots/codekk_image.gif -------------------------------------------------------------------------------- /screenshots/job_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/screenshots/job_image.gif -------------------------------------------------------------------------------- /screenshots/op_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/screenshots/op_image.gif -------------------------------------------------------------------------------- /screenshots/opa_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7449/codeKK-Android/5077997f52328fc3f491f64ad92d9dd28eda4ce7/screenshots/opa_image.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------