├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── sbingo
│ │ │ │ └── wanandroid_mvvm
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── HttpConstants.kt
│ │ │ │ ├── WanApplication.kt
│ │ │ │ ├── adapter
│ │ │ │ ├── HomeAdapter.kt
│ │ │ │ ├── KnowledgeTreeAdapter.kt
│ │ │ │ ├── KnowledgeViewPagerAdapter.kt
│ │ │ │ ├── NavigationChapterAdapter.kt
│ │ │ │ ├── NavigationWebsiteAdapter.kt
│ │ │ │ ├── ProjectViewPagerAdapter.kt
│ │ │ │ └── WeChatViewPagerAdapter.kt
│ │ │ │ ├── base
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── BaseBindingActivity.kt
│ │ │ │ ├── BaseBindingFragment.kt
│ │ │ │ ├── BaseFragment.kt
│ │ │ │ ├── RequestState.kt
│ │ │ │ └── paging
│ │ │ │ │ ├── BaseDataSourceFactory.kt
│ │ │ │ │ ├── BaseItemKeyedDataSource.kt
│ │ │ │ │ ├── BasePagingAdapter.kt
│ │ │ │ │ ├── BasePagingRepository.kt
│ │ │ │ │ ├── BasePagingViewModel.kt
│ │ │ │ │ └── Listing.kt
│ │ │ │ ├── data
│ │ │ │ └── http
│ │ │ │ │ ├── HttpManager.kt
│ │ │ │ │ ├── HttpResponse.kt
│ │ │ │ │ ├── RxHttpObserver.kt
│ │ │ │ │ ├── WanApi.kt
│ │ │ │ │ └── interceptor
│ │ │ │ │ ├── CacheInterceptor.kt
│ │ │ │ │ ├── CookieInterceptor.kt
│ │ │ │ │ ├── HeaderInterceptor.kt
│ │ │ │ │ └── LoggingInterceptor.kt
│ │ │ │ ├── model
│ │ │ │ └── Beans.kt
│ │ │ │ ├── paging
│ │ │ │ ├── factory
│ │ │ │ │ ├── HomeDataSourceFactory.kt
│ │ │ │ │ ├── KnowledgeDataSourceFactory.kt
│ │ │ │ │ ├── KnowledgeTreeSourceFactory.kt
│ │ │ │ │ ├── NavigationDataSourceFactory.kt
│ │ │ │ │ ├── ProjectDataSourceFactory.kt
│ │ │ │ │ └── WXDataSourceFactory.kt
│ │ │ │ ├── repository
│ │ │ │ │ ├── HomeRepository.kt
│ │ │ │ │ ├── KnowledgeRepository.kt
│ │ │ │ │ ├── KnowledgeTreeRepository.kt
│ │ │ │ │ ├── NavigationRepository.kt
│ │ │ │ │ ├── ProjectRepository.kt
│ │ │ │ │ └── WXRepository.kt
│ │ │ │ └── source
│ │ │ │ │ ├── HomeDataSource.kt
│ │ │ │ │ ├── KnowledgeDataSource.kt
│ │ │ │ │ ├── KnowledgeTreeDataSource.kt
│ │ │ │ │ ├── NavigationDataSource.kt
│ │ │ │ │ ├── ProjectDataSource.kt
│ │ │ │ │ └── WXDataSource.kt
│ │ │ │ ├── repository
│ │ │ │ ├── ProjectRepository.kt
│ │ │ │ └── WeChatRepository.kt
│ │ │ │ ├── ui
│ │ │ │ ├── activity
│ │ │ │ │ ├── KnowledgeActivity.kt
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── ScanActivity.kt
│ │ │ │ │ └── WebActivity.kt
│ │ │ │ └── fragment
│ │ │ │ │ ├── HomeFragment.kt
│ │ │ │ │ ├── KnowledgeFragment.kt
│ │ │ │ │ ├── KnowledgeTreeFragment.kt
│ │ │ │ │ ├── NavigationFragment.kt
│ │ │ │ │ ├── ProjectArticleFragment.kt
│ │ │ │ │ ├── ProjectFragment.kt
│ │ │ │ │ ├── WXArticleFragment.kt
│ │ │ │ │ └── WeChatFragment.kt
│ │ │ │ ├── utils
│ │ │ │ ├── ExecutorUtils.kt
│ │ │ │ ├── Ext.kt
│ │ │ │ ├── ImageLoader.kt
│ │ │ │ ├── Listeners.kt
│ │ │ │ ├── NetUtils.kt
│ │ │ │ ├── PreferenceUtils.kt
│ │ │ │ └── ToastUtils.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── HomeViewModel.kt
│ │ │ │ ├── KnowledgeTreeeViewModel.kt
│ │ │ │ ├── KnowledgeViewModel.kt
│ │ │ │ ├── NavigationViewModel.kt
│ │ │ │ ├── ProjectArticleViewModel.kt
│ │ │ │ ├── ProjectViewModel.kt
│ │ │ │ ├── WXArticleViewModel.kt
│ │ │ │ └── WeChatViewModel.kt
│ │ └── res
│ │ │ ├── anim
│ │ │ ├── push_in.xml
│ │ │ └── push_out.xml
│ │ │ ├── drawable
│ │ │ ├── bg_fresh.xml
│ │ │ ├── bg_placeholder.xml
│ │ │ ├── ic_close.xml
│ │ │ ├── ic_dashboard_black_24dp.xml
│ │ │ ├── ic_home_black_24dp.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_navigation.xml
│ │ │ ├── ic_notifications_black_24dp.xml
│ │ │ ├── ic_project.xml
│ │ │ ├── ic_scan.xml
│ │ │ ├── ic_search.xml
│ │ │ ├── ic_wechat.xml
│ │ │ ├── nav_item_color_selector.xml
│ │ │ ├── pic_error.png
│ │ │ ├── progress.xml
│ │ │ └── round_toast_bg.xml
│ │ │ ├── layout
│ │ │ ├── activity_knowledge_tree.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_web.xml
│ │ │ ├── banner_layout.xml
│ │ │ ├── container.xml
│ │ │ ├── empty_view.xml
│ │ │ ├── error_view.xml
│ │ │ ├── footer_item.xml
│ │ │ ├── fragment_navigation.xml
│ │ │ ├── fragment_tab_vp.xml
│ │ │ ├── home_item.xml
│ │ │ ├── knowledge_tree_item.xml
│ │ │ ├── loading_view.xml
│ │ │ ├── nav_header.xml
│ │ │ ├── navigation_tv.xml
│ │ │ ├── navigation_website.xml
│ │ │ ├── navigation_website_tv.xml
│ │ │ ├── refresh_layout.xml
│ │ │ ├── toast_layout.xml
│ │ │ ├── toolbar.xml
│ │ │ └── web_error_page.xml
│ │ │ ├── menu
│ │ │ ├── menu_activity_main.xml
│ │ │ ├── menu_navigation.xml
│ │ │ └── navigation_bottom.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_wan_mvvm.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_wan_mvvm.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_wan_mvvm.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_wan_mvvm.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_wan_mvvm.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ └── test
│ │ └── java
│ │ └── com
│ │ └── sbingo
│ │ └── wanandroid_mvvm
│ │ └── ExampleUnitTest.kt
└── wanandroid-mvvm.jks
├── build.gradle
├── config.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── arc.PNG
├── wan2.png
├── wan3.png
├── wan4.png
├── wan5.png
├── wan6.png
├── wan7.png
├── wan8.png
└── wan9.png
├── settings.gradle
└── 技术分析.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WanAndroid-MVVM
2 | 玩 Android 客户端,采用 kotlin 语言,Material Design 风格,根据 MVVM 架构使用 Jetpack 架构组件搭建了整套框架
3 |
4 | 持续更新ing~
5 |
6 | ### Jetpack 使用情况
7 | - DataBinding
8 | - Navigation
9 | - LiveData
10 | - ViewModel
11 | - Paging
12 | - Room (以后可能会用到)
13 |
14 | ### 架构图
15 |
16 |
17 | ### 技术分析
18 | [MVVM 架构解析及 Jetpack 架构组件的使用](https://blog.csdn.net/recordGrowth/article/details/90377318)
19 |
20 | ### 效果图
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ### 感谢
30 | 数据来源 : [玩 Android](https://www.wanandroid.com/blog/show/2)
31 |
32 | ### 关于作者
33 | 个人网站 : [binaryshao](http://sbingo666.com/)
34 |
35 | Github : [binaryshao](https://github.com/binaryshao)
36 |
37 | CSDN : [binaryshao](https://blog.csdn.net/recordgrowth)
38 |
39 | Email : shaojianbin@sbingo666.com
40 |
41 | 公众号 :
42 |
43 | 
44 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion rootProject.ext.android.compileSdkVersion
8 | defaultConfig {
9 | applicationId "com.sbingo.wanandroid_mvvm"
10 | minSdkVersion rootProject.ext.android.minSdkVersion
11 | targetSdkVersion rootProject.ext.android.targetSdkVersion
12 | versionCode rootProject.ext.android.versionCode
13 | versionName rootProject.ext.android.versionName
14 | multiDexEnabled true
15 | }
16 |
17 | signingConfigs {
18 | release {
19 | storeFile file(RELEASE_STORE_FILE)
20 | storePassword RELEASE_STORE_PASSWORD
21 | keyAlias RELEASE_KEY_ALIAS
22 | keyPassword RELEASE_KEY_PASSWORD
23 | v1SigningEnabled true
24 | v2SigningEnabled true
25 | }
26 | }
27 |
28 | buildTypes {
29 | debug {
30 | signingConfig signingConfigs.release
31 | minifyEnabled false
32 | }
33 | release {
34 | signingConfig signingConfigs.release
35 | minifyEnabled false
36 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
37 | }
38 | }
39 |
40 | dataBinding {
41 | enabled = true
42 | }
43 | }
44 |
45 | dependencies {
46 | implementation fileTree(dir: 'libs', include: ['*.jar'])
47 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
48 | implementation rootProject.ext.libraries
49 | kapt "com.github.bumptech.glide:compiler:$rootProject.ext.dependVersion.glide"
50 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
51 | }
52 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm
2 |
3 | /**
4 | * Author: Sbingo666
5 | * Date: 2019/4/25
6 | */
7 | object Constants {
8 |
9 | const val WEB_TITLE = "web_title"
10 | const val WEB_URL = "web_url"
11 |
12 | const val KNOWLEDGE = "knowledge"
13 |
14 | const val PERMISSION_CODE = 100
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/HttpConstants.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm
2 |
3 | import com.sbingo.wanandroid_mvvm.utils.PreferenceUtils
4 |
5 | /**
6 | * Author: Sbingo666
7 | * Date: 2019/4/3
8 | */
9 | object HttpConstants {
10 |
11 | const val BASE_URL = "https://www.wanandroid.com/"
12 | const val NETWORK_TIME = 60
13 | const val CACHE_NAME = "wanandroid-mvvm"
14 | const val MAX_CACHE_SIZE = 50
15 |
16 | const val SAVE_USER_LOGIN_KEY = "user/login"
17 | const val SAVE_USER_REGISTER_KEY = "user/register"
18 |
19 | val LOGIN_REQUIRED_URLS = arrayOf("lg/collect", "lg/uncollect", "lg/todo")
20 |
21 | const val COOKIE_HEADER_RESPONSE = "set-cookie"
22 | const val COOKIE_HEADER_REQUEST = "Cookie"
23 |
24 | fun encodeCookie(cookies: List): String {
25 | val sb = StringBuilder()
26 | val set = HashSet()
27 | cookies
28 | .map { cookie ->
29 | cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
30 | }
31 | .forEach {
32 | it.filterNot { set.contains(it) }.forEach { set.add(it) }
33 | }
34 | val ite = set.iterator()
35 | while (ite.hasNext()) {
36 | val cookie = ite.next()
37 | sb.append(cookie).append(";")
38 | }
39 | val last = sb.lastIndexOf(";")
40 | if (sb.length - 1 == last) {
41 | sb.deleteCharAt(last)
42 | }
43 | return sb.toString()
44 | }
45 |
46 | fun saveCookie(url: String?, host: String?, cookies: String) {
47 | url ?: return
48 | PreferenceUtils(url, cookies)
49 | host ?: return
50 | PreferenceUtils(host, cookies)
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/WanApplication.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm
2 |
3 |
4 | import android.app.Activity
5 | import android.app.Application
6 | import android.os.Bundle
7 | import androidx.multidex.MultiDexApplication
8 | import com.orhanobut.logger.LogLevel
9 | import com.orhanobut.logger.Logger
10 |
11 | /**
12 | * Author: Sbingo666
13 | * Date: 2019/4/3
14 | */
15 | class WanApplication : MultiDexApplication() {
16 |
17 | companion object {
18 | lateinit var instance: Application
19 | }
20 |
21 | override fun onCreate() {
22 | super.onCreate()
23 | instance = this
24 | initLogger()
25 | registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks)
26 | }
27 |
28 | private fun initLogger() {
29 | val lever = if (BuildConfig.DEBUG) LogLevel.FULL else LogLevel.NONE
30 | Logger.init("Sbingo666")
31 | .methodCount(0)
32 | .hideThreadInfo()
33 | .logLevel(lever)
34 | }
35 |
36 | private val mActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
37 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
38 | Logger.d(activity.componentName.className + " onCreated")
39 | }
40 |
41 | override fun onActivityStarted(activity: Activity) {
42 | }
43 |
44 | override fun onActivityResumed(activity: Activity) {
45 | Logger.d(activity.componentName.className + " onResumed")
46 | }
47 |
48 | override fun onActivityPaused(activity: Activity) {
49 |
50 | }
51 |
52 | override fun onActivityStopped(activity: Activity) {
53 |
54 | }
55 |
56 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
57 |
58 | }
59 |
60 | override fun onActivityDestroyed(activity: Activity) {
61 | Logger.d(activity.componentName.className + " onDestroyed")
62 | }
63 | }
64 |
65 |
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/HomeAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import android.content.Intent
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.ImageView
8 | import androidx.core.text.HtmlCompat
9 | import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
10 | import androidx.recyclerview.widget.DiffUtil
11 | import cn.bingoogolapple.bgabanner.BGABanner
12 | import com.sbingo.wanandroid_mvvm.Constants
13 | import com.sbingo.wanandroid_mvvm.R
14 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingAdapter
15 | import com.sbingo.wanandroid_mvvm.model.Article
16 | import com.sbingo.wanandroid_mvvm.model.Banner
17 | import com.sbingo.wanandroid_mvvm.ui.activity.WebActivity
18 | import com.sbingo.wanandroid_mvvm.utils.ImageLoader
19 | import io.reactivex.Observable
20 |
21 | /**
22 | * Author: Sbingo666
23 | * Date: 2019/4/18
24 | */
25 | class HomeAdapter(retryCallback: () -> Unit) : BasePagingAdapter(diffCallback, retryCallback) {
26 |
27 | private val TYPE_BANNER = 10
28 |
29 | companion object {
30 | val diffCallback = object : DiffUtil.ItemCallback() {
31 | override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean =
32 | oldItem == newItem
33 |
34 | override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean =
35 | oldItem.id == newItem.id
36 | }
37 | }
38 |
39 | override fun getItemLayout() = R.layout.home_item
40 |
41 | override fun bind(holder: ViewHolder, item: Article, position: Int) {
42 | holder.run {
43 | toVisibility(R.id.fresh, item.fresh)
44 | toVisibility(R.id.thumbnail, item.envelopePic.isNotBlank())
45 | toVisibility(R.id.desc, item.desc.isNotBlank())
46 | toVisibility(R.id.on_top, item.isTop)
47 | setText(R.id.author, item.author)
48 | setText(R.id.date, item.niceDate)
49 | setText(R.id.title, HtmlCompat.fromHtml(item.title, FROM_HTML_MODE_LEGACY).toString())
50 | setText(
51 | R.id.chapter, when {
52 | item.superChapterName.isNotBlank() and item.chapterName.isNotBlank() ->
53 | "${item.superChapterName} / ${item.chapterName}"
54 | item.superChapterName.isNotBlank() -> item.superChapterName
55 | item.chapterName.isNotBlank() -> item.chapterName
56 | else -> ""
57 | }
58 | )
59 | setText(R.id.desc, item.desc)
60 | }
61 | if (item.envelopePic.isNotBlank()) {
62 | context.let { ImageLoader.load(it, item.envelopePic, holder.getView(R.id.thumbnail) as ImageView) }
63 | }
64 | holder.itemView.setOnClickListener {
65 | Intent(context, WebActivity::class.java).run {
66 | putExtra(Constants.WEB_TITLE, item.title)
67 | putExtra(Constants.WEB_URL, item.link)
68 | context.startActivity(this)
69 | }
70 | }
71 | }
72 |
73 | override fun getItemViewType(position: Int): Int {
74 | if (position == 0 && !getItem(position)?.bannerData.isNullOrEmpty()) {
75 | return TYPE_BANNER
76 | }
77 | return super.getItemViewType(position)
78 | }
79 |
80 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
81 | if (viewType == TYPE_BANNER) {
82 | return BannerViewHolder.create(parent)
83 | }
84 | return super.onCreateViewHolder(parent, viewType)
85 | }
86 |
87 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
88 | if (getItemViewType(position) == TYPE_BANNER) {
89 | (holder as BannerViewHolder).bind(getItem(position)!!.bannerData)
90 | }
91 | super.onBindViewHolder(holder, position)
92 | }
93 |
94 | class BannerViewHolder(view: View) : ViewHolder(view) {
95 | fun bind(bannerData: List) {
96 | val banner = getView(R.id.banner) as BGABanner
97 | banner.run {
98 | setAdapter { _, itemView, model, _ ->
99 | ImageLoader.load(this.context, model as String?, itemView as ImageView?)
100 | }
101 | setDelegate { _, _, _, position ->
102 | if (bannerData.isNotEmpty()) {
103 | val item = bannerData[position]
104 | Intent(context, WebActivity::class.java).run {
105 | putExtra(Constants.WEB_TITLE, item.title)
106 | putExtra(Constants.WEB_URL, item.url)
107 | context.startActivity(this)
108 | }
109 | }
110 | }
111 | val imageList = ArrayList()
112 | val titleList = ArrayList()
113 | Observable.fromIterable(bannerData)
114 | .subscribe { list ->
115 | imageList.add(list.imagePath)
116 | titleList.add(list.title)
117 | }
118 | setAutoPlayAble(imageList.size > 1)
119 | setData(imageList, titleList)
120 | }
121 | }
122 |
123 | companion object {
124 | fun create(parent: ViewGroup): BannerViewHolder {
125 | val view = LayoutInflater.from(parent.context)
126 | .inflate(R.layout.banner_layout, parent, false)
127 | return BannerViewHolder(view)
128 | }
129 | }
130 | }
131 |
132 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/KnowledgeTreeAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import android.content.Intent
4 | import androidx.core.text.HtmlCompat
5 | import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
6 | import androidx.recyclerview.widget.DiffUtil
7 | import com.sbingo.wanandroid_mvvm.Constants
8 | import com.sbingo.wanandroid_mvvm.R
9 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingAdapter
10 | import com.sbingo.wanandroid_mvvm.model.Chapter
11 | import com.sbingo.wanandroid_mvvm.ui.activity.KnowledgeActivity
12 |
13 | /**
14 | * Author: Sbingo666
15 | * Date: 2019/4/26
16 | */
17 | class KnowledgeTreeAdapter(retryCallback: () -> Unit) : BasePagingAdapter(diffCallback, retryCallback) {
18 |
19 | companion object {
20 | val diffCallback = object : DiffUtil.ItemCallback() {
21 | override fun areContentsTheSame(oldItem: Chapter, newItem: Chapter): Boolean =
22 | oldItem == newItem
23 |
24 | override fun areItemsTheSame(oldItem: Chapter, newItem: Chapter): Boolean =
25 | oldItem.id == newItem.id
26 | }
27 | }
28 |
29 | override fun getItemLayout() = R.layout.knowledge_tree_item
30 |
31 | override fun bind(holder: ViewHolder, item: Chapter, position: Int) {
32 | holder.run {
33 | setText(R.id.title, item.name)
34 | setText(
35 | R.id.subtitle,
36 | item.children.joinToString(
37 | " ",
38 | transform = { child -> HtmlCompat.fromHtml(child.name, FROM_HTML_MODE_LEGACY).toString() })
39 | )
40 | }
41 | holder.itemView.setOnClickListener {
42 | Intent(context, KnowledgeActivity::class.java).run {
43 | putExtra(Constants.KNOWLEDGE, item)
44 | context.startActivity(this)
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/KnowledgeViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import androidx.core.text.HtmlCompat
4 | import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
5 | import androidx.fragment.app.Fragment
6 | import androidx.fragment.app.FragmentManager
7 | import androidx.fragment.app.FragmentStatePagerAdapter
8 | import com.sbingo.wanandroid_mvvm.model.Chapter
9 | import com.sbingo.wanandroid_mvvm.ui.fragment.KnowledgeFragment
10 |
11 | /**
12 | * Author: Sbingo666
13 | * Date: 2019/4/26
14 | */
15 | class KnowledgeViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
16 |
17 | private var fragments = mutableListOf()
18 | private var list = mutableListOf()
19 |
20 | override fun getItem(position: Int): Fragment = fragments[position]
21 |
22 | override fun getCount(): Int = fragments.size
23 |
24 | override fun getPageTitle(position: Int): CharSequence? =
25 | HtmlCompat.fromHtml(list[position].name, FROM_HTML_MODE_LEGACY).toString()
26 |
27 | fun setData(data: List) {
28 | list.run {
29 | clear()
30 | addAll(data)
31 | forEach {
32 | fragments.add(KnowledgeFragment(it.id))
33 | }
34 | }
35 | notifyDataSetChanged()
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/NavigationChapterAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import androidx.core.content.ContextCompat
4 | import androidx.recyclerview.widget.DiffUtil
5 | import com.sbingo.wanandroid_mvvm.R
6 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingAdapter
7 | import com.sbingo.wanandroid_mvvm.model.Navigation
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/28
12 | */
13 | class NavigationChapterAdapter(retryCallback: () -> Unit, private val listener: OnItemClickListener) :
14 | BasePagingAdapter(diffCallback, retryCallback) {
15 |
16 | private var positionChecked: Int = 0
17 |
18 | companion object {
19 | val diffCallback = object : DiffUtil.ItemCallback() {
20 | override fun areContentsTheSame(oldItem: Navigation, newItem: Navigation): Boolean =
21 | oldItem == newItem
22 |
23 | override fun areItemsTheSame(oldItem: Navigation, newItem: Navigation): Boolean =
24 | oldItem.cid == newItem.cid
25 | }
26 | }
27 |
28 | override fun getItemLayout() = R.layout.navigation_tv
29 |
30 | override fun bind(holder: ViewHolder, item: Navigation, position: Int) {
31 | holder.run {
32 | setText(R.id.chapter, item.name)
33 | }
34 | holder.itemView.run {
35 | setOnClickListener {
36 | listener.onItemClicked(position)
37 | setCheckedPosition(position)
38 | }
39 | setBackgroundColor(
40 | ContextCompat.getColor(
41 | context,
42 | if (position == positionChecked) {
43 | R.color.colorPrimaryLight
44 | } else {
45 | R.color.White
46 | }
47 | )
48 | )
49 | }
50 | }
51 |
52 | fun setCheckedPosition(position: Int) {
53 | positionChecked = position
54 | notifyDataSetChanged()
55 | }
56 |
57 | interface OnItemClickListener {
58 | fun onItemClicked(position: Int) {
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/NavigationWebsiteAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import android.content.Intent
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.recyclerview.widget.DiffUtil
8 | import com.sbingo.wanandroid_mvvm.Constants
9 | import com.sbingo.wanandroid_mvvm.R
10 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingAdapter
11 | import com.sbingo.wanandroid_mvvm.model.Article
12 | import com.sbingo.wanandroid_mvvm.model.Navigation
13 | import com.sbingo.wanandroid_mvvm.ui.activity.WebActivity
14 | import com.zhy.view.flowlayout.FlowLayout
15 | import com.zhy.view.flowlayout.TagAdapter
16 | import com.zhy.view.flowlayout.TagFlowLayout
17 |
18 | /**
19 | * Author: Sbingo666
20 | * Date: 2019/4/28
21 | */
22 | class NavigationWebsiteAdapter(retryCallback: () -> Unit) : BasePagingAdapter(diffCallback, retryCallback) {
23 |
24 | companion object {
25 | val diffCallback = object : DiffUtil.ItemCallback() {
26 | override fun areContentsTheSame(oldItem: Navigation, newItem: Navigation): Boolean =
27 | oldItem == newItem
28 |
29 | override fun areItemsTheSame(oldItem: Navigation, newItem: Navigation): Boolean =
30 | oldItem.cid == newItem.cid
31 | }
32 | }
33 |
34 | override fun getItemLayout() = R.layout.navigation_website
35 |
36 | override fun bind(holder: ViewHolder, item: Navigation, position: Int) {
37 | holder.run {
38 | setText(R.id.chapter, item.name)
39 | val flowLayout = getView(R.id.websites) as TagFlowLayout
40 | flowLayout.run {
41 | adapter = object : TagAdapter(item.articles) {
42 | override fun getView(parent: FlowLayout?, position: Int, article: Article?): View? {
43 | val tv = LayoutInflater.from(parent?.context).inflate(
44 | R.layout.navigation_website_tv,
45 | flowLayout,
46 | false
47 | ) as TextView
48 | article ?: return null
49 | tv.text = article.title
50 | return tv
51 | }
52 | }
53 | setOnTagClickListener { _, position, _ ->
54 | val article = item.articles[position]
55 | Intent(context, WebActivity::class.java).run {
56 | putExtra(Constants.WEB_TITLE, article.title)
57 | putExtra(Constants.WEB_URL, article.link)
58 | context.startActivity(this)
59 | }
60 | true
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/ProjectViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import androidx.core.text.HtmlCompat
4 | import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
5 | import androidx.fragment.app.Fragment
6 | import androidx.fragment.app.FragmentManager
7 | import androidx.fragment.app.FragmentStatePagerAdapter
8 | import com.sbingo.wanandroid_mvvm.model.Chapter
9 | import com.sbingo.wanandroid_mvvm.ui.fragment.ProjectArticleFragment
10 |
11 | /**
12 | * Author: Sbingo666
13 | * Date: 2019/4/23
14 | */
15 | class ProjectViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
16 |
17 | private var fragments = mutableListOf()
18 | private var list = mutableListOf()
19 |
20 | override fun getItem(position: Int): Fragment = fragments[position]
21 |
22 | override fun getCount(): Int = fragments.size
23 |
24 | override fun getPageTitle(position: Int): CharSequence? =
25 | HtmlCompat.fromHtml(list[position].name, FROM_HTML_MODE_LEGACY).toString()
26 |
27 | fun setData(data: List) {
28 | list.run {
29 | clear()
30 | addAll(data)
31 | forEach {
32 | fragments.add(ProjectArticleFragment(it.id))
33 | }
34 | }
35 | notifyDataSetChanged()
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/adapter/WeChatViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.adapter
2 |
3 | import androidx.core.text.HtmlCompat
4 | import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
5 | import androidx.fragment.app.Fragment
6 | import androidx.fragment.app.FragmentManager
7 | import androidx.fragment.app.FragmentStatePagerAdapter
8 | import com.sbingo.wanandroid_mvvm.model.Chapter
9 | import com.sbingo.wanandroid_mvvm.ui.fragment.WXArticleFragment
10 |
11 | /**
12 | * Author: Sbingo666
13 | * Date: 2019/4/22
14 | */
15 | class WeChatViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
16 |
17 | private var fragments = mutableListOf()
18 | private var list = mutableListOf()
19 |
20 | override fun getItem(position: Int): Fragment = fragments[position]
21 |
22 | override fun getCount(): Int = fragments.size
23 |
24 | override fun getPageTitle(position: Int): CharSequence? =
25 | HtmlCompat.fromHtml(list[position].name, FROM_HTML_MODE_LEGACY).toString()
26 |
27 | fun setData(data: List) {
28 | list.run {
29 | clear()
30 | addAll(data)
31 | forEach {
32 | fragments.add(WXArticleFragment(it.id))
33 | }
34 | }
35 | notifyDataSetChanged()
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base
2 |
3 | import android.app.AlertDialog
4 | import android.os.Build
5 | import android.os.Bundle
6 | import android.view.MenuItem
7 | import androidx.annotation.NonNull
8 | import androidx.appcompat.app.AppCompatActivity
9 | import androidx.core.content.ContextCompat
10 | import androidx.core.content.PermissionChecker
11 | import androidx.lifecycle.LiveData
12 | import androidx.lifecycle.Observer
13 | import com.classic.common.MultipleStatusView
14 | import com.sbingo.wanandroid_mvvm.Constants
15 | import com.sbingo.wanandroid_mvvm.R
16 | import com.sbingo.wanandroid_mvvm.ui.activity.ScanActivity
17 | import com.sbingo.wanandroid_mvvm.utils.Listeners
18 | import java.util.*
19 |
20 | /**
21 | * Author: Sbingo666
22 | * Date: 2019/4/3
23 | */
24 | abstract class BaseActivity : AppCompatActivity() {
25 |
26 | protected abstract var layoutId: Int
27 |
28 | protected var multipleStatusView: MultipleStatusView? = null
29 |
30 | protected abstract fun initData()
31 |
32 | protected abstract fun subscribeUi()
33 |
34 | private lateinit var permissionListener: Listeners.PermissionListener
35 | private lateinit var deniedPermissions: HashMap>
36 | private var showPermissionDialogOnDenied: Boolean = true
37 |
38 | override fun onCreate(savedInstanceState: Bundle?) {
39 | super.onCreate(savedInstanceState)
40 | if (this !is BaseBindingActivity<*> && this !is ScanActivity) {
41 | setContentView(layoutId)
42 | }
43 | initData()
44 | multipleStatusView?.setOnClickListener {
45 | when (multipleStatusView?.viewStatus) {
46 | MultipleStatusView.STATUS_ERROR, MultipleStatusView.STATUS_EMPTY -> onRetry()
47 | }
48 | }
49 | subscribeUi()
50 | }
51 |
52 | open fun onRetry() {
53 |
54 | }
55 |
56 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
57 | when (item.itemId) {
58 | android.R.id.home -> onBackPressed()
59 | }
60 | return super.onOptionsItemSelected(item)
61 | }
62 |
63 | protected fun handleData(liveData: LiveData>, action: (T) -> Unit) =
64 | liveData.observe(this, Observer { result ->
65 | if (result.isLoading()) {
66 | multipleStatusView?.showLoading()
67 | } else if (result.isSuccess()) {
68 | if (result?.data != null) {
69 | multipleStatusView?.showContent()
70 | action(result.data)
71 | } else {
72 | multipleStatusView?.showEmpty()
73 | }
74 | } else if (result.isError()) {
75 | multipleStatusView?.showError()
76 | }
77 | })
78 |
79 | protected fun checkPermissions(permissions: Array, permissionsCN: Array, reasons: Array, listener: Listeners.PermissionListener) {
80 | permissionListener = listener
81 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
82 | permissionListener.onGranted()
83 | } else {
84 | deniedPermissions = HashMap()
85 | permissions.forEachIndexed { index, s ->
86 | if (ContextCompat.checkSelfPermission(this, s) != PermissionChecker.PERMISSION_GRANTED) {
87 | deniedPermissions[s] = arrayOf(permissionsCN[index], reasons[index])
88 | }
89 | }
90 | if (deniedPermissions.isEmpty()) {
91 | permissionListener.onGranted()
92 | } else {
93 | val permissionsSb = StringBuilder()
94 | val reasonsSb = StringBuilder()
95 | deniedPermissions.forEach {
96 | if (shouldShowRequestPermissionRationale(it.key)) {
97 | //用户拒绝过权限该方法就会返回 true,选择不再提示后返回 false
98 | permissionsSb.append(it.value[0]).append(",")
99 | reasonsSb.append(it.value[1]).append("\n")
100 | }
101 | }
102 | if (permissionsSb.isNotBlank() && reasonsSb.isNotBlank()) {
103 | showPermissionDialogOnDenied = false
104 | permissionsSb.deleteCharAt(permissionsSb.length - 1)
105 | reasonsSb.deleteCharAt(reasonsSb.length - 1)
106 | showPermissionDeniedDialog(permissionsSb.toString(), reasonsSb.toString()) {
107 | requestPermissions(deniedPermissions.keys.toTypedArray(), Constants.PERMISSION_CODE)
108 | permissionListener.onShowReason()
109 | }
110 | } else {
111 | requestPermissions(deniedPermissions.keys.toTypedArray(), Constants.PERMISSION_CODE)
112 | }
113 | }
114 | }
115 | }
116 |
117 | override fun onRequestPermissionsResult(requestCode: Int, @NonNull permissions: Array, @NonNull grantResults: IntArray) {
118 | when (requestCode) {
119 | Constants.PERMISSION_CODE -> if (grantResults.isNotEmpty()) {
120 | val deniedAgainPermissions = ArrayList()
121 | for (i in grantResults.indices) {
122 | val permission = permissions[i]
123 | val grantResult = grantResults[i]
124 | if (grantResult != PermissionChecker.PERMISSION_GRANTED) {
125 | deniedAgainPermissions.add(permission)
126 | }
127 | }
128 | if (deniedAgainPermissions.isEmpty()) {
129 | permissionListener.onGranted()
130 | } else {
131 | deniedAgainPermissions.forEach { now ->
132 | deniedPermissions.forEach { old ->
133 | if (now == old.key) {
134 | if (showPermissionDialogOnDenied) {
135 | showPermissionDeniedDialog(old.value[0], old.value[1]) { permissionListener.onDenied(deniedAgainPermissions) }
136 | } else {
137 | showPermissionDialogOnDenied = true
138 | permissionListener.onDenied(deniedAgainPermissions)
139 | }
140 | }
141 | }
142 | }
143 | }
144 | }
145 | }
146 | }
147 |
148 | private fun showPermissionDeniedDialog(p: String, reason: String, action: () -> Unit) {
149 | AlertDialog.Builder(this)
150 | .setTitle("缺少【$p】权限")
151 | .setMessage(reason)
152 | .setCancelable(false)
153 | .setPositiveButton(
154 | R.string.i_know
155 | ) { _, _ -> action() }
156 | .show()
157 | }
158 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/BaseBindingActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base
2 |
3 | import androidx.databinding.ViewDataBinding
4 |
5 | /**
6 | * Author: Sbingo666
7 | * Date: 2019/4/15
8 | */
9 | abstract class BaseBindingActivity : BaseActivity() {
10 |
11 | override var layoutId = 0
12 |
13 | protected abstract var binding: T
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/BaseBindingFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base
2 |
3 | import androidx.databinding.ViewDataBinding
4 |
5 | /**
6 | * Author: Sbingo666
7 | * Date: 2019/4/15
8 | */
9 | abstract class BaseBindingFragment : BaseFragment() {
10 |
11 | override var layoutId = 0
12 |
13 | protected abstract var binding: T
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/RequestState.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base
2 |
3 | /**
4 | * Author: Sbingo666
5 | * Date: 2019/4/12
6 | */
7 |
8 | enum class Status {
9 | LOADING,
10 | SUCCESS,
11 | ERROR,
12 | }
13 |
14 | data class RequestState(val status: Status, val data: T?, val message: String? = null) {
15 | companion object {
16 | fun loading(data: T? = null) = RequestState(Status.LOADING, data)
17 |
18 | fun success(data: T? = null) = RequestState(Status.SUCCESS, data)
19 |
20 | fun error(msg: String? = null, data: T? = null) = RequestState(Status.ERROR, data, msg)
21 | }
22 |
23 | fun isLoading(): Boolean = status == Status.LOADING
24 | fun isSuccess(): Boolean = status == Status.SUCCESS
25 | fun isError(): Boolean = status == Status.ERROR
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/paging/BaseDataSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base.paging
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.paging.DataSource
5 |
6 | /**
7 | * Author: Sbingo666
8 | * Date: 2019/4/12
9 | */
10 | abstract class BaseDataSourceFactory : DataSource.Factory() {
11 |
12 | val sourceLivaData = MutableLiveData>()
13 |
14 | override fun create(): BaseItemKeyedDataSource {
15 | val dataSource: BaseItemKeyedDataSource = createDataSource()
16 | sourceLivaData.postValue(dataSource)
17 | return dataSource
18 | }
19 |
20 | abstract fun createDataSource(): BaseItemKeyedDataSource
21 |
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/paging/BaseItemKeyedDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base.paging
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.paging.ItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.base.RequestState
6 | import com.sbingo.wanandroid_mvvm.utils.ExecutorUtils
7 |
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/12
12 | */
13 | abstract class BaseItemKeyedDataSource : ItemKeyedDataSource() {
14 | private var retry: (() -> Any)? = null
15 | private var retryExecutor = ExecutorUtils.NETWORK_IO
16 |
17 | val loadMoreStatus by lazy {
18 | MutableLiveData>()
19 | }
20 |
21 | val refreshStatus by lazy {
22 | MutableLiveData>()
23 | }
24 |
25 | fun retryFailed() {
26 | val preRetry = retry
27 | retry = null
28 | preRetry.let {
29 | retryExecutor.execute {
30 | it?.invoke()
31 | }
32 | }
33 | }
34 |
35 | override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
36 | refreshStatus.postValue(RequestState.loading())
37 | onLoadInitial(params, callback)
38 | }
39 |
40 | override fun loadAfter(params: LoadParams, callback: LoadCallback) {
41 | loadMoreStatus.postValue(RequestState.loading())
42 | onLoadAfter(params, callback)
43 | }
44 |
45 | override fun loadBefore(params: LoadParams, callback: LoadCallback) {
46 | }
47 |
48 | fun refreshSuccess(isDataEmpty: Boolean = false) {
49 | refreshStatus.postValue(RequestState.success(isDataEmpty))
50 | retry = null
51 | }
52 |
53 | fun loadMoreSuccess() {
54 | retry = null
55 | loadMoreStatus.postValue(RequestState.success())
56 | }
57 |
58 | fun loadMoreFailed(msg: String?, params: LoadParams, callback: LoadCallback) {
59 | loadMoreStatus.postValue(RequestState.error())
60 | retry = {
61 | loadAfter(params, callback)
62 | }
63 | }
64 |
65 | fun refreshFailed(msg: String?, params: LoadInitialParams, callback: LoadInitialCallback) {
66 | refreshStatus.postValue(RequestState.error())
67 | retry = {
68 | loadInitial(params, callback)
69 | }
70 | }
71 |
72 |
73 | override fun getKey(item: T) = setKey(item)
74 |
75 | abstract fun setKey(item: T): Int
76 |
77 | abstract fun onLoadAfter(params: LoadParams, callback: LoadCallback)
78 |
79 | abstract fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback)
80 | }
81 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/paging/BasePagingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base.paging
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.TextView
8 | import androidx.paging.PagedListAdapter
9 | import androidx.recyclerview.widget.DiffUtil
10 | import androidx.recyclerview.widget.RecyclerView
11 | import com.sbingo.wanandroid_mvvm.R
12 | import com.sbingo.wanandroid_mvvm.base.RequestState
13 |
14 | /**
15 | * Author: Sbingo666
16 | * Date: 2019/4/12
17 | */
18 | abstract class BasePagingAdapter(diffCallback: DiffUtil.ItemCallback, private val retryCallback: () -> Unit) :
19 | PagedListAdapter(diffCallback) {
20 |
21 | private val TYPE_ITEM = 0
22 | private val TYPE_FOOTER = 1
23 | private var requestState: RequestState? = null
24 | lateinit var context: Context
25 |
26 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
27 | context = parent.context
28 | return when (viewType) {
29 | TYPE_ITEM -> ViewHolder(LayoutInflater.from(context).inflate(getItemLayout(), parent, false))
30 | TYPE_FOOTER -> FooterViewHolder.create(parent, retryCallback)
31 | else -> throw IllegalArgumentException("未知 view type = $viewType")
32 | }
33 | }
34 |
35 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
36 | when (getItemViewType(position)) {
37 | TYPE_ITEM -> bind(holder, getItem(position)!!, position)
38 | TYPE_FOOTER -> (holder as FooterViewHolder).bindTo(requestState)
39 | }
40 | }
41 |
42 | open class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
43 | private val map = mutableMapOf()
44 |
45 | fun getView(id: Int): View? {
46 | var view = map[id]
47 | if (view == null) {
48 | view = this.view.findViewById(id)
49 | map[id] = view
50 | }
51 | return view
52 | }
53 |
54 | fun setText(id: Int, string: String?) {
55 | val textView = getView(id)
56 | if (textView is TextView) {
57 | textView.text = string
58 | }
59 | }
60 |
61 | fun toVisibility(id: Int, constraint: Boolean) {
62 | getView(id)?.visibility = if (constraint) {
63 | View.VISIBLE
64 | } else {
65 | View.GONE
66 | }
67 | }
68 | }
69 |
70 | class FooterViewHolder(view: View, private val retryCallback: () -> Unit) : ViewHolder(view) {
71 |
72 | init {
73 | getView(R.id.retry_button)?.setOnClickListener {
74 | retryCallback()
75 | }
76 | }
77 |
78 | fun bindTo(requestState: RequestState?) {
79 | toVisibility(R.id.progress_bar, requestState!!.isLoading())
80 | toVisibility(R.id.retry_button, requestState.isError())
81 | toVisibility(R.id.msg, requestState.isLoading())
82 | setText(R.id.msg, "加载中...")
83 | }
84 |
85 | companion object {
86 | fun create(parent: ViewGroup, retryCallback: () -> Unit): FooterViewHolder {
87 | val view = LayoutInflater.from(parent.context)
88 | .inflate(R.layout.footer_item, parent, false)
89 | return FooterViewHolder(view, retryCallback)
90 | }
91 | }
92 | }
93 |
94 | private fun hasFooter() =
95 | if (requestState == null)
96 | false
97 | else {
98 | !requestState?.isSuccess()!!
99 | }
100 |
101 | override fun getItemViewType(position: Int): Int {
102 | return if (hasFooter() && position == itemCount - 1) {
103 | TYPE_FOOTER
104 | } else {
105 | TYPE_ITEM
106 | }
107 | }
108 |
109 | override fun getItemCount(): Int {
110 | return super.getItemCount() + if (hasFooter()) 1 else 0
111 | }
112 |
113 | fun setRequestState(newRequestState: RequestState) {
114 | val previousState = this.requestState
115 | val hadExtraRow = hasFooter()
116 | this.requestState = newRequestState
117 | val hasExtraRow = hasFooter()
118 | if (hadExtraRow != hasExtraRow) {
119 | if (hadExtraRow) {
120 | notifyItemRemoved(super.getItemCount())
121 | } else {
122 | notifyItemInserted(super.getItemCount())
123 | }
124 | } else if (hasExtraRow && previousState != newRequestState) {
125 | notifyItemChanged(itemCount - 1)
126 | }
127 | }
128 |
129 | abstract fun getItemLayout(): Int
130 |
131 | abstract fun bind(holder: ViewHolder, item: T, position: Int)
132 |
133 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/paging/BasePagingRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base.paging
2 |
3 | import androidx.lifecycle.Transformations
4 | import androidx.paging.Config
5 | import androidx.paging.toLiveData
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/12
10 | */
11 | abstract class BasePagingRepository {
12 |
13 | fun getData(pageSize: Int): Listing {
14 |
15 | val sourceFactory = createDataBaseFactory()
16 | val pagedList = sourceFactory.toLiveData(
17 | config = Config(
18 | pageSize = pageSize,
19 | enablePlaceholders = false,
20 | initialLoadSizeHint = pageSize * 2
21 | )
22 | )
23 | val refreshState = Transformations.switchMap(sourceFactory.sourceLivaData) { it.refreshStatus }
24 | val networkStatus = Transformations.switchMap(sourceFactory.sourceLivaData) { it.loadMoreStatus }
25 |
26 | return Listing(
27 | pagedList,
28 | networkStatus,
29 | refreshState,
30 | refresh = {
31 | sourceFactory.sourceLivaData.value?.invalidate()
32 | },
33 | retry = {
34 | sourceFactory.sourceLivaData.value?.retryFailed()
35 | }
36 | )
37 | }
38 |
39 | abstract fun createDataBaseFactory(): BaseDataSourceFactory
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/paging/BasePagingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base.paging
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.Transformations
5 | import androidx.lifecycle.ViewModel
6 |
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/12
11 | */
12 | open class BasePagingViewModel(repository: BasePagingRepository) : ViewModel() {
13 |
14 | private val pageSize = MutableLiveData()
15 | private val repoResult = Transformations.map(pageSize) {
16 | repository.getData(it)
17 | }
18 | val pagedList = Transformations.switchMap(repoResult) { it.pagedList }
19 | val networkState = Transformations.switchMap(repoResult) { it.networkState }
20 | val refreshState = Transformations.switchMap(repoResult) { it.refreshState }
21 |
22 | fun refresh() {
23 | repoResult.value?.refresh?.invoke()
24 | }
25 |
26 | fun initLoad(newSize: Int = 10): Boolean {
27 | if (pageSize.value == newSize)
28 | return false
29 | pageSize.value = newSize
30 | return true
31 | }
32 |
33 | fun retry() {
34 | repoResult.value?.retry?.invoke()
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/base/paging/Listing.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.base.paging
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.paging.PagedList
5 | import com.sbingo.wanandroid_mvvm.base.RequestState
6 |
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/12
11 | */
12 | data class Listing(
13 | val pagedList: LiveData>,
14 | val networkState: LiveData>,
15 | val refreshState: LiveData>,
16 | val refresh: () -> Unit,
17 | val retry: () -> Unit
18 | )
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/HttpManager.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http
2 |
3 | import com.orhanobut.logger.Logger
4 | import com.sbingo.wanandroid_mvvm.BuildConfig
5 | import com.sbingo.wanandroid_mvvm.HttpConstants
6 | import com.sbingo.wanandroid_mvvm.WanApplication
7 | import com.sbingo.wanandroid_mvvm.data.http.interceptor.CacheInterceptor
8 | import com.sbingo.wanandroid_mvvm.data.http.interceptor.CookieInterceptor
9 | import com.sbingo.wanandroid_mvvm.data.http.interceptor.HeaderInterceptor
10 | import com.sbingo.wanandroid_mvvm.data.http.interceptor.LoggingInterceptor
11 | import okhttp3.Cache
12 | import okhttp3.OkHttpClient
13 | import retrofit2.Retrofit
14 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
15 | import retrofit2.converter.gson.GsonConverterFactory
16 | import retrofit2.converter.scalars.ScalarsConverterFactory
17 | import java.io.File
18 | import java.util.concurrent.TimeUnit
19 |
20 | /**
21 | * Author: Sbingo666
22 | * Date: 2019/4/3
23 | */
24 | class HttpManager private constructor() {
25 |
26 | companion object {
27 | @Volatile
28 | private var instance: HttpManager? = null
29 |
30 | fun getInstance(): HttpManager {
31 | return instance ?: synchronized(this) {
32 | instance ?: HttpManager().also { instance = it }
33 | }
34 | }
35 | }
36 |
37 | internal val wanApi: WanApi by lazy {
38 | create(HttpConstants.BASE_URL, WanApi::class.java)
39 | }
40 |
41 | private fun create(baseUrl: String, c: Class): T {
42 | return Retrofit.Builder()
43 | .baseUrl(baseUrl)
44 | .client(provideOKHttpClient())
45 | .addConverterFactory(ScalarsConverterFactory.create())
46 | .addConverterFactory(GsonConverterFactory.create())
47 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
48 | .build()
49 | .create(c)
50 | }
51 |
52 | private class MyLog : LoggingInterceptor.Logger {
53 | override fun log(message: String) {
54 | Logger.d(message)
55 | }
56 | }
57 |
58 | private fun provideOKHttpClient(): OkHttpClient {
59 | val loggingInterceptor = LoggingInterceptor(MyLog())
60 | loggingInterceptor.level =
61 | if (BuildConfig.DEBUG) LoggingInterceptor.Level.BODY else LoggingInterceptor.Level.NONE
62 | val cache = Cache(
63 | File(WanApplication.instance.cacheDir, HttpConstants.CACHE_NAME),
64 | HttpConstants.MAX_CACHE_SIZE.toLong()
65 | )
66 | val builder = OkHttpClient.Builder()
67 | .connectTimeout(HttpConstants.NETWORK_TIME.toLong(), TimeUnit.SECONDS)
68 | .readTimeout(HttpConstants.NETWORK_TIME.toLong(), TimeUnit.SECONDS)
69 | .writeTimeout(HttpConstants.NETWORK_TIME.toLong(), TimeUnit.SECONDS)
70 | .retryOnConnectionFailure(true)
71 | .addInterceptor(HeaderInterceptor())
72 | .addInterceptor(CacheInterceptor())
73 | .addInterceptor(CookieInterceptor())
74 | .addInterceptor(loggingInterceptor)
75 | .cache(cache)
76 | return builder.build()
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/HttpResponse.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http
2 |
3 |
4 | /**
5 | * Author: Sbingo666
6 | * Date: 2019/4/4
7 | */
8 | data class HttpResponse(
9 | var data: T?,
10 | var errorCode: Int,
11 | var errorMsg: String?
12 | )
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/RxHttpObserver.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http
2 |
3 | import com.sbingo.wanandroid_mvvm.R
4 | import com.sbingo.wanandroid_mvvm.WanApplication
5 | import com.sbingo.wanandroid_mvvm.utils.ExecutorUtils
6 | import com.sbingo.wanandroid_mvvm.utils.NetUtils
7 | import com.sbingo.wanandroid_mvvm.utils.ToastUtils
8 | import io.reactivex.Observer
9 | import io.reactivex.disposables.Disposable
10 |
11 | abstract class RxHttpObserver : Observer {
12 |
13 | override fun onSubscribe(d: Disposable) {
14 | if (!NetUtils.isConnected(WanApplication.instance)) {
15 | onError(RuntimeException(WanApplication.instance.getString(R.string.network_error)))
16 | }
17 | }
18 |
19 | override fun onError(e: Throwable) {
20 | e.message?.let {
21 | ExecutorUtils.main_thread(Runnable { ToastUtils.show(it) })
22 | }
23 | }
24 |
25 | override fun onNext(it: T) {
26 | //业务失败
27 | val result = it as? HttpResponse<*>
28 | if (result?.errorCode != 0) {
29 | onError(
30 | RuntimeException(
31 | if (result?.errorMsg.isNullOrBlank())
32 | WanApplication.instance.getString(R.string.business_error)
33 | else {
34 | result?.errorMsg
35 | }
36 | )
37 | )
38 | }
39 | }
40 |
41 | override fun onComplete() {
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/WanApi.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http
2 |
3 | import com.sbingo.wanandroid_mvvm.model.*
4 | import io.reactivex.Observable
5 | import retrofit2.http.GET
6 | import retrofit2.http.Path
7 | import retrofit2.http.Query
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/3
12 | */
13 | interface WanApi {
14 |
15 | /**
16 | * 首页文章列表
17 | */
18 | @GET("article/list/{pageNo}/json")
19 | fun getArticles(@Path("pageNo") pageNo: Int): Observable>>
20 |
21 | /**
22 | * 置顶文章列表
23 | */
24 | @GET("article/top/json")
25 | fun getTopArticles(): Observable>>
26 |
27 | /**
28 | * 首页 banner
29 | */
30 | @GET("banner/json")
31 | fun getBanner(): Observable>>
32 |
33 | /**
34 | * 公众号列表
35 | */
36 | @GET("wxarticle/chapters/json")
37 | fun getWXChapters(): Observable>>
38 |
39 | /**
40 | * 查看某个公众号历史数据
41 | */
42 | @GET("wxarticle/list/{id}/{pageNo}/json")
43 | fun getWXArticles(@Path("id") id: Int, @Path("pageNo") pageNo: Int): Observable>>
44 |
45 | /**
46 | * 项目类目列表
47 | */
48 | @GET("project/tree/json")
49 | fun getProjects(): Observable>>
50 |
51 | /**
52 | * 项目文章列表
53 | */
54 | @GET("project/list/{pageNo}/json")
55 | fun getProjectArticles(@Path("pageNo") pageNo: Int, @Query("cid") cid: Int): Observable>>
56 |
57 | /**
58 | * 导航数据
59 | */
60 | @GET("navi/json")
61 | fun getProjectArticles(): Observable>>
62 |
63 | /**
64 | * 知识体系
65 | */
66 | @GET("tree/json")
67 | fun getKnowledgeTree(): Observable>>
68 |
69 | /**
70 | * 知识体系文章列表
71 | */
72 | @GET("article/list/{pageNo}/json")
73 | fun getKnowledgeArticles(@Path("pageNo") pageNo: Int, @Query("cid") cid: Int): Observable>>
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/interceptor/CacheInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http.interceptor
2 |
3 | import com.sbingo.wanandroid_mvvm.WanApplication
4 | import com.sbingo.wanandroid_mvvm.utils.NetUtils
5 | import okhttp3.CacheControl
6 | import okhttp3.Interceptor
7 | import okhttp3.Response
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/3
12 | */
13 | class CacheInterceptor : Interceptor {
14 |
15 | override fun intercept(chain: Interceptor.Chain): Response {
16 | var request = chain.request()
17 | if (!NetUtils.isConnected(WanApplication.instance)) {
18 | request = request.newBuilder()
19 | .cacheControl(CacheControl.FORCE_CACHE)
20 | .build()
21 | }
22 | val response = chain.proceed(request)
23 | if (NetUtils.isConnected(WanApplication.instance)) {
24 | val maxAge = 60 * 3
25 | response.newBuilder()
26 | .removeHeader("Pragma")
27 | .header("Cache-Control", "public, max-age=$maxAge")
28 | .build()
29 | } else {
30 | val maxStale = 60 * 60 * 24 * 7
31 | response.newBuilder()
32 | .removeHeader("Pragma")
33 | .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
34 | .build()
35 | }
36 | return response
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/interceptor/CookieInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http.interceptor
2 |
3 | import com.sbingo.wanandroid_mvvm.HttpConstants
4 | import okhttp3.Interceptor
5 | import okhttp3.Response
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/3
10 | */
11 | class CookieInterceptor : Interceptor {
12 |
13 | override fun intercept(chain: Interceptor.Chain): Response {
14 | val request = chain.request()
15 | val response = chain.proceed(request)
16 | val requestUrl = request.url().toString()
17 | val host = request.url().host()
18 | if ((requestUrl.contains(HttpConstants.SAVE_USER_LOGIN_KEY)
19 | || requestUrl.contains(HttpConstants.SAVE_USER_REGISTER_KEY))
20 | && response.headers(HttpConstants.COOKIE_HEADER_RESPONSE).isNotEmpty()
21 | ) {
22 | val cookies = response.headers(HttpConstants.COOKIE_HEADER_RESPONSE)
23 | val cookie = HttpConstants.encodeCookie(cookies)
24 | HttpConstants.saveCookie(requestUrl, host, cookie)
25 | }
26 | return response
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/data/http/interceptor/HeaderInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.data.http.interceptor
2 |
3 | import com.sbingo.wanandroid_mvvm.HttpConstants
4 | import com.sbingo.wanandroid_mvvm.utils.PreferenceUtils
5 | import okhttp3.Interceptor
6 | import okhttp3.Response
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/3
11 | */
12 | class HeaderInterceptor : Interceptor {
13 |
14 | override fun intercept(chain: Interceptor.Chain): Response {
15 | val request = chain.request()
16 | val builder = request.newBuilder()
17 | builder.addHeader("Content-type", "application/json; charset=utf-8")
18 |
19 | val host = request.url().host()
20 | val requestUrl = request.url().toString()
21 | val loginRequired = HttpConstants.LOGIN_REQUIRED_URLS.firstOrNull { url -> requestUrl.contains(url) }
22 |
23 | if (host.isNotEmpty() && !loginRequired.isNullOrEmpty()) {
24 | val spHost: String by PreferenceUtils(host, "")
25 | val cookie: String = if (spHost.isNotEmpty()) spHost else ""
26 | if (cookie.isNotEmpty()) {
27 | builder.addHeader(HttpConstants.COOKIE_HEADER_REQUEST, cookie)
28 | }
29 | }
30 | return chain.proceed(builder.build())
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/model/Beans.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.model
2 |
3 | import java.io.Serializable
4 |
5 |
6 | /**
7 | * Author: Sbingo666
8 | * Date: 2019/4/4
9 | */
10 | data class Page(
11 | var curPage: Int,
12 | var datas: MutableList,
13 | var offset: Int,
14 | var pageCount: Int,
15 | var size: Int,
16 | var total: Int,
17 | var over: Boolean
18 | )
19 |
20 | data class Article(
21 | var apkLink: String,
22 | var author: String,
23 | var chapterId: Int,
24 | var chapterName: String,
25 | var collect: Boolean,
26 | var courseId: Int,
27 | var desc: String,
28 | var envelopePic: String,
29 | var fresh: Boolean,
30 | var id: Int,
31 | var link: String,
32 | var niceDate: String,
33 | var origin: String,
34 | var projectLink: String,
35 | var publishTime: Long,
36 | var superChapterId: Int,
37 | var superChapterName: String,
38 | var title: String,
39 | var type: Int,
40 | var userId: Int,
41 | var visible: Int,
42 | var zan: Int,
43 | var tags: List,
44 | var isTop: Boolean,
45 | var bannerData: List
46 | )
47 |
48 | data class Tag(
49 | var name: String,
50 | var url: String
51 | )
52 |
53 | data class Chapter(
54 | var children: List,
55 | var courseId: Int,
56 | var id: Int,
57 | var name: String,
58 | var order: Int,
59 | var parentChapterId: Int,
60 | var userControlSetTop: Boolean,
61 | var visible: Int
62 | ) : Serializable
63 |
64 | data class Navigation(
65 | var articles: List,
66 | var cid: Int,
67 | var name: String
68 | )
69 |
70 | data class Banner(
71 | var desc: String,
72 | var id: Int,
73 | var imagePath: String,
74 | var isVisible: Int,
75 | var order: Int,
76 | var title: String,
77 | var type: Int,
78 | var url: String
79 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/factory/HomeDataSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.factory
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.source.HomeDataSource
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/17
12 | */
13 | class HomeDataSourceFactory(private val httpManager: HttpManager) : BaseDataSourceFactory() {
14 |
15 | override fun createDataSource(): BaseItemKeyedDataSource {
16 | return HomeDataSource(httpManager)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/factory/KnowledgeDataSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.factory
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.source.KnowledgeDataSource
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/26
12 | */
13 | class KnowledgeDataSourceFactory(private val httpManager: HttpManager, private val knowledgeId: Int) :
14 | BaseDataSourceFactory() {
15 |
16 | override fun createDataSource(): BaseItemKeyedDataSource {
17 | return KnowledgeDataSource(httpManager, knowledgeId)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/factory/KnowledgeTreeSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.factory
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Chapter
7 | import com.sbingo.wanandroid_mvvm.paging.source.KnowledgeTreeDataSource
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/26
12 | */
13 | class KnowledgeTreeSourceFactory(private val httpManager: HttpManager) :
14 | BaseDataSourceFactory() {
15 |
16 | override fun createDataSource(): BaseItemKeyedDataSource {
17 | return KnowledgeTreeDataSource(httpManager)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/factory/NavigationDataSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.factory
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Navigation
7 | import com.sbingo.wanandroid_mvvm.paging.source.NavigationDataSource
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/28
12 | */
13 | class NavigationDataSourceFactory(private val httpManager: HttpManager) :
14 | BaseDataSourceFactory() {
15 |
16 | override fun createDataSource(): BaseItemKeyedDataSource {
17 | return NavigationDataSource(httpManager)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/factory/ProjectDataSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.factory
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.source.ProjectDataSource
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/23
12 | */
13 | class ProjectDataSourceFactory(private val httpManager: HttpManager, private val projectId: Int) :
14 | BaseDataSourceFactory() {
15 |
16 | override fun createDataSource(): BaseItemKeyedDataSource {
17 | return ProjectDataSource(httpManager, projectId)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/factory/WXDataSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.factory
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.source.WXDataSource
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/23
12 | */
13 | class WXDataSourceFactory(private val httpManager: HttpManager, private val wxId: Int) :
14 | BaseDataSourceFactory() {
15 |
16 | override fun createDataSource(): BaseItemKeyedDataSource {
17 | return WXDataSource(httpManager, wxId)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/repository/HomeRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.repository
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingRepository
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.factory.HomeDataSourceFactory
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/17
12 | */
13 | class HomeRepository(private val httpManager: HttpManager) : BasePagingRepository() {
14 |
15 | override fun createDataBaseFactory(): BaseDataSourceFactory {
16 | return HomeDataSourceFactory(httpManager)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/repository/KnowledgeRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.repository
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingRepository
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.factory.KnowledgeDataSourceFactory
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/26
12 | */
13 | class KnowledgeRepository(private val httpManager: HttpManager, private val knowledgeId: Int) : BasePagingRepository() {
14 |
15 | override fun createDataBaseFactory(): BaseDataSourceFactory {
16 | return KnowledgeDataSourceFactory(httpManager, knowledgeId)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/repository/KnowledgeTreeRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.repository
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingRepository
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Chapter
7 | import com.sbingo.wanandroid_mvvm.paging.factory.KnowledgeTreeSourceFactory
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/26
12 | */
13 | class KnowledgeTreeRepository(private val httpManager: HttpManager) : BasePagingRepository() {
14 |
15 | override fun createDataBaseFactory(): BaseDataSourceFactory {
16 | return KnowledgeTreeSourceFactory(httpManager)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/repository/NavigationRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.repository
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingRepository
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Navigation
7 | import com.sbingo.wanandroid_mvvm.paging.factory.NavigationDataSourceFactory
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/28
12 | */
13 | class NavigationRepository(private val httpManager: HttpManager) : BasePagingRepository() {
14 |
15 | override fun createDataBaseFactory(): BaseDataSourceFactory {
16 | return NavigationDataSourceFactory(httpManager)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/repository/ProjectRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.repository
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingRepository
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.factory.ProjectDataSourceFactory
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/23
12 | */
13 | class ProjectArticleRepository(private val httpManager: HttpManager, private val projectId: Int) : BasePagingRepository() {
14 |
15 | override fun createDataBaseFactory(): BaseDataSourceFactory {
16 | return ProjectDataSourceFactory(httpManager, projectId)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/repository/WXRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.repository
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseDataSourceFactory
4 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingRepository
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.paging.factory.WXDataSourceFactory
8 |
9 | /**
10 | * Author: Sbingo666
11 | * Date: 2019/4/23
12 | */
13 | class WXRepository(private val httpManager: HttpManager, private val wxId: Int) : BasePagingRepository() {
14 |
15 | override fun createDataBaseFactory(): BaseDataSourceFactory {
16 | return WXDataSourceFactory(httpManager, wxId)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/source/HomeDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.source
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
4 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
5 | import com.sbingo.wanandroid_mvvm.data.http.HttpResponse
6 | import com.sbingo.wanandroid_mvvm.model.Article
7 | import com.sbingo.wanandroid_mvvm.model.Banner
8 | import com.sbingo.wanandroid_mvvm.model.Page
9 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
10 | import io.reactivex.Observable
11 | import io.reactivex.functions.Function3
12 |
13 | /**
14 | * Author: Sbingo666
15 | * Date: 2019/4/17
16 | */
17 | class HomeDataSource(private val httpManager: HttpManager) : BaseItemKeyedDataSource() {
18 |
19 | var pageNo = 0
20 |
21 | override fun setKey(item: Article) = item.id
22 |
23 | override fun onLoadAfter(params: LoadParams, callback: LoadCallback) {
24 | httpManager.wanApi.getArticles(pageNo)
25 | .asyncSubscribe({
26 | pageNo = it.data?.curPage!!
27 | loadMoreSuccess()
28 | callback.onResult(it.data?.datas!!)
29 | }, {
30 | loadMoreFailed(it.message, params, callback)
31 | })
32 | }
33 |
34 | override fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
35 | Observable.zip(httpManager.wanApi.getBanner(),
36 | httpManager.wanApi.getTopArticles(),
37 | httpManager.wanApi.getArticles(pageNo),
38 | Function3>, HttpResponse>, HttpResponse>, HttpResponse>>
39 | { t1, t2, t3 ->
40 | t1.data?.let {
41 | //动态构造一个 Article,将 banner 数据放入其中
42 | val article = t3.data?.datas!![0]
43 | article.bannerData = it
44 | t3.data?.datas?.add(0, article)
45 | }
46 | t2.data?.let {
47 | it.forEach { it.isTop = true }
48 | t3.data?.datas?.addAll(1, it)
49 | }
50 | t3
51 | })
52 | .asyncSubscribe({
53 | pageNo = it.data?.curPage!!
54 | refreshSuccess(it.data?.datas!!.isEmpty())
55 | callback.onResult(it.data?.datas!!)
56 | }, {
57 | refreshFailed(it.message, params, callback)
58 | })
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/source/KnowledgeDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.source
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
4 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
5 | import com.sbingo.wanandroid_mvvm.model.Article
6 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/26
11 | */
12 | class KnowledgeDataSource(private val httpManager: HttpManager, private val knowledgeId: Int) : BaseItemKeyedDataSource() {
13 |
14 | var pageNo = 0
15 |
16 | override fun setKey(item: Article) = item.id
17 |
18 | override fun onLoadAfter(params: LoadParams, callback: LoadCallback) {
19 | httpManager.wanApi.getKnowledgeArticles(pageNo, knowledgeId)
20 | .asyncSubscribe({
21 | pageNo += 1
22 | loadMoreSuccess()
23 | callback.onResult(it.data?.datas!!)
24 | }, {
25 | loadMoreFailed(it.message, params, callback)
26 | })
27 | }
28 |
29 | override fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
30 | httpManager.wanApi.getKnowledgeArticles(pageNo, knowledgeId)
31 | .asyncSubscribe({
32 | pageNo += 1
33 | refreshSuccess(it.data?.datas!!.isEmpty())
34 | callback.onResult(it.data?.datas!!)
35 | }, {
36 | refreshFailed(it.message, params, callback)
37 | })
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/source/KnowledgeTreeDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.source
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
4 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
5 | import com.sbingo.wanandroid_mvvm.model.Chapter
6 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/26
11 | */
12 | class KnowledgeTreeDataSource(private val httpManager: HttpManager) : BaseItemKeyedDataSource() {
13 |
14 |
15 | override fun setKey(item: Chapter) = item.id
16 |
17 | override fun onLoadAfter(params: LoadParams, callback: LoadCallback) {
18 | loadMoreSuccess()
19 | }
20 |
21 | override fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
22 | httpManager.wanApi.getKnowledgeTree()
23 | .asyncSubscribe({
24 | refreshSuccess(it.data!!.isEmpty())
25 | callback.onResult(it.data!!)
26 | }, {
27 | refreshFailed(it.message, params, callback)
28 | })
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/source/NavigationDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.source
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
4 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
5 | import com.sbingo.wanandroid_mvvm.model.Navigation
6 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/28
11 | */
12 | class NavigationDataSource(private val httpManager: HttpManager) : BaseItemKeyedDataSource() {
13 |
14 |
15 | override fun setKey(item: Navigation) = item.cid
16 |
17 | override fun onLoadAfter(params: LoadParams, callback: LoadCallback) {
18 | loadMoreSuccess()
19 | }
20 |
21 | override fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
22 | httpManager.wanApi.getProjectArticles()
23 | .asyncSubscribe({
24 | refreshSuccess(it.data!!.isEmpty())
25 | callback.onResult(it.data!!)
26 | }, {
27 | refreshFailed(it.message, params, callback)
28 | })
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/source/ProjectDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.source
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
4 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
5 | import com.sbingo.wanandroid_mvvm.model.Article
6 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/23
11 | */
12 | class ProjectDataSource(private val httpManager: HttpManager, private val projectId: Int) :
13 | BaseItemKeyedDataSource() {
14 |
15 | var pageNo = 1
16 |
17 | override fun setKey(item: Article) = item.id
18 |
19 | override fun onLoadAfter(params: LoadParams, callback: LoadCallback) {
20 | httpManager.wanApi.getProjectArticles(pageNo, projectId)
21 | .asyncSubscribe({
22 | pageNo += 1
23 | loadMoreSuccess()
24 | callback.onResult(it.data?.datas!!)
25 | }, {
26 | loadMoreFailed(it.message, params, callback)
27 | })
28 | }
29 |
30 | override fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
31 | httpManager.wanApi.getProjectArticles(pageNo, projectId)
32 | .asyncSubscribe({
33 | pageNo += 1
34 | refreshSuccess(it.data?.datas!!.isEmpty())
35 | callback.onResult(it.data?.datas!!)
36 | }, {
37 | refreshFailed(it.message, params, callback)
38 | })
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/paging/source/WXDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.paging.source
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BaseItemKeyedDataSource
4 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
5 | import com.sbingo.wanandroid_mvvm.model.Article
6 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/23
11 | */
12 | class WXDataSource(private val httpManager: HttpManager, private val wxId: Int) : BaseItemKeyedDataSource() {
13 |
14 | var pageNo = 1
15 |
16 | override fun setKey(item: Article) = item.id
17 |
18 | override fun onLoadAfter(params: LoadParams, callback: LoadCallback) {
19 | httpManager.wanApi.getWXArticles(wxId, pageNo)
20 | .asyncSubscribe({
21 | pageNo += 1
22 | loadMoreSuccess()
23 | callback.onResult(it.data?.datas!!)
24 | }, {
25 | loadMoreFailed(it.message, params, callback)
26 | })
27 | }
28 |
29 | override fun onLoadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {
30 | httpManager.wanApi.getWXArticles(wxId, pageNo)
31 | .asyncSubscribe({
32 | pageNo += 1
33 | refreshSuccess(it.data?.datas!!.isEmpty())
34 | callback.onResult(it.data?.datas!!)
35 | }, {
36 | refreshFailed(it.message, params, callback)
37 | })
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/repository/ProjectRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.repository
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import com.sbingo.wanandroid_mvvm.base.RequestState
6 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
7 | import com.sbingo.wanandroid_mvvm.model.Chapter
8 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
9 |
10 | /**
11 | * Author: Sbingo666
12 | * Date: 2019/4/23
13 | */
14 | class ProjectRepository(private val httpManager: HttpManager) {
15 |
16 | fun getProjects(): LiveData>> {
17 | val liveData = MutableLiveData>>()
18 | liveData.value = RequestState.loading()
19 | httpManager.wanApi.getProjects()
20 | .asyncSubscribe({
21 | liveData.postValue(RequestState.success(it.data))
22 | }, {
23 | liveData.postValue(RequestState.error(it.message))
24 | })
25 | return liveData
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/repository/WeChatRepository.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.repository
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import com.sbingo.wanandroid_mvvm.base.RequestState
6 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
7 | import com.sbingo.wanandroid_mvvm.model.Chapter
8 | import com.sbingo.wanandroid_mvvm.utils.asyncSubscribe
9 |
10 | /**
11 | * Author: Sbingo666
12 | * Date: 2019/4/22
13 | */
14 | class WeChatRepository(private val httpManager: HttpManager) {
15 |
16 | fun getWXChapters(): LiveData>> {
17 | val liveData = MutableLiveData>>()
18 | liveData.value = RequestState.loading()
19 | httpManager.wanApi.getWXChapters()
20 | .asyncSubscribe({
21 | liveData.postValue(RequestState.success(it.data))
22 | }, {
23 | liveData.postValue(RequestState.error(it.message))
24 | })
25 | return liveData
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/activity/KnowledgeActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.activity
2 |
3 | import com.google.android.material.tabs.TabLayout
4 | import com.sbingo.wanandroid_mvvm.Constants
5 | import com.sbingo.wanandroid_mvvm.R
6 | import com.sbingo.wanandroid_mvvm.adapter.KnowledgeViewPagerAdapter
7 | import com.sbingo.wanandroid_mvvm.base.BaseActivity
8 | import com.sbingo.wanandroid_mvvm.model.Chapter
9 | import kotlinx.android.synthetic.main.fragment_tab_vp.*
10 | import kotlinx.android.synthetic.main.toolbar.*
11 |
12 | /**
13 | * Author: Sbingo666
14 | * Date: 2019/4/26
15 | */
16 | class KnowledgeActivity : BaseActivity() {
17 |
18 | private val knowledge by lazy {
19 | intent?.extras?.getSerializable(Constants.KNOWLEDGE) as Chapter
20 | }
21 |
22 | private val adapter by lazy {
23 | KnowledgeViewPagerAdapter(supportFragmentManager)
24 | }
25 |
26 | override var layoutId = R.layout.activity_knowledge_tree
27 |
28 | override fun initData() {
29 | viewPager.adapter = adapter
30 | tabLayout.run {
31 | setupWithViewPager(viewPager)
32 | addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
33 | override fun onTabReselected(tab: TabLayout.Tab?) {
34 | }
35 |
36 | override fun onTabUnselected(tab: TabLayout.Tab?) {
37 | }
38 |
39 | override fun onTabSelected(tab: TabLayout.Tab?) {
40 | tab?.let {
41 | viewPager.setCurrentItem(it.position, false)
42 | }
43 | }
44 | })
45 | }
46 | toolbar.run {
47 | title = knowledge.name
48 | setSupportActionBar(this)
49 | supportActionBar?.setDisplayHomeAsUpEnabled(true)
50 | }
51 | knowledge.let {
52 | adapter.setData(it.children)
53 | }
54 | }
55 |
56 | override fun subscribeUi() {
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.activity
2 |
3 | import android.content.Intent
4 | import android.view.KeyEvent
5 | import android.view.Menu
6 | import android.view.MenuItem
7 | import androidx.appcompat.app.ActionBarDrawerToggle
8 | import androidx.core.view.GravityCompat
9 | import androidx.fragment.app.Fragment
10 | import com.google.android.material.bottomnavigation.BottomNavigationView
11 | import com.google.android.material.bottomnavigation.LabelVisibilityMode.LABEL_VISIBILITY_LABELED
12 | import com.google.android.material.navigation.NavigationView
13 | import com.sbingo.wanandroid_mvvm.R
14 | import com.sbingo.wanandroid_mvvm.base.BaseActivity
15 | import com.sbingo.wanandroid_mvvm.ui.fragment.*
16 | import com.sbingo.wanandroid_mvvm.utils.ToastUtils
17 | import kotlinx.android.synthetic.main.activity_main.*
18 | import kotlinx.android.synthetic.main.toolbar.*
19 |
20 |
21 | class MainActivity : BaseActivity() {
22 |
23 | private var lastExitTime: Long = 0
24 | private var mCurrentFragment: Fragment? = null
25 | private var index = 0
26 | private var fragmentTag: String? = null
27 | private val fragmentNames = arrayOf(
28 | HomeFragment::class.java.name, WeChatFragment::class.java.name, ProjectFragment::class.java.name,
29 | NavigationFragment::class.java.name, KnowledgeTreeFragment::class.java.name
30 | )
31 | private val bottomTitles = arrayOf(
32 | R.string.home, R.string.wechat, R.string.project, R.string.navigation, R.string.knowledge_tree
33 | )
34 |
35 | override var layoutId = R.layout.activity_main
36 |
37 | override fun initData() {
38 | setSupportActionBar(toolbar)
39 | initDrawerLayout()
40 | initNav()
41 | initNavBottom()
42 | bottomNav()
43 | }
44 |
45 | override fun subscribeUi() {
46 | }
47 |
48 | private fun initDrawerLayout() {
49 | drawer_layout.run {
50 | val toggle = ActionBarDrawerToggle(
51 | this@MainActivity,
52 | this,
53 | toolbar,
54 | R.string.app_name,
55 | R.string.app_name
56 | )
57 | addDrawerListener(toggle)
58 | toggle.syncState()
59 | }
60 | }
61 |
62 | private fun initNav() {
63 | val navListener = NavigationView.OnNavigationItemSelectedListener {
64 | ToastUtils.show(it.title.toString())
65 | when (it.itemId) {
66 | R.id.favorites -> {
67 | }
68 | R.id.todo -> {
69 | }
70 | R.id.night_mode -> {
71 | }
72 | R.id.setting -> {
73 | }
74 | R.id.logout -> {
75 | }
76 | R.id.about -> {
77 | }
78 | }
79 | drawer_layout.closeDrawer(GravityCompat.START)
80 | true
81 | }
82 | nav_view.run {
83 | setNavigationItemSelectedListener(navListener)
84 | }
85 | }
86 |
87 | private fun initNavBottom() {
88 | val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
89 | when (item.itemId) {
90 | R.id.home -> index = 0
91 | R.id.wechat -> index = 1
92 | R.id.project -> index = 2
93 | R.id.navigation -> index = 3
94 | R.id.knowledge_tree -> index = 4
95 | }
96 | bottomNav()
97 | true
98 | }
99 | navigation_bottom.run {
100 | labelVisibilityMode = LABEL_VISIBILITY_LABELED
101 | setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
102 | }
103 | }
104 |
105 | private fun bottomNav() {
106 | toolbar.title = getString(
107 | if (index == 0) {
108 | R.string.app_name
109 | } else {
110 | bottomTitles[index]
111 | }
112 | )
113 | fragmentTag = fragmentNames[index]
114 | val fragment = getFragmentByTag(fragmentTag!!)
115 | showFragment(mCurrentFragment, fragment, fragmentTag!!)
116 | }
117 |
118 | private fun getFragmentByTag(name: String): Fragment {
119 | var fragment = supportFragmentManager.findFragmentByTag(name)
120 | if (fragment != null) {
121 | return fragment
122 | } else {
123 | try {
124 | fragment = Class.forName(name).newInstance() as Fragment
125 | } catch (e: Exception) {
126 | fragment = HomeFragment()
127 | }
128 | }
129 | return fragment!!
130 | }
131 |
132 | private fun showFragment(from: Fragment?, to: Fragment, tag: String) {
133 | val transaction = supportFragmentManager.beginTransaction()
134 | if (from == null) {
135 | if (to.isAdded) {
136 | transaction.show(to)
137 | } else {
138 | transaction.add(R.id.container, to, tag)
139 | }
140 | } else {
141 | if (to.isAdded) {
142 | transaction.hide(from).show(to)
143 | } else {
144 | transaction.hide(from).add(R.id.container, to, tag)
145 | }
146 | }
147 | transaction.commit()
148 | mCurrentFragment = to
149 | }
150 |
151 | override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
152 | if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
153 | drawer_layout.closeDrawer(GravityCompat.START)
154 | return true
155 | } else if (KeyEvent.KEYCODE_BACK == keyCode) {
156 | val currentTime = System.currentTimeMillis()
157 | if (currentTime - lastExitTime > 1500) {
158 | ToastUtils.show(getString(R.string.exit_hint))
159 | lastExitTime = currentTime
160 | return true
161 | } else {
162 | finish()
163 | }
164 | }
165 | return super.onKeyDown(keyCode, event)
166 | }
167 |
168 | override fun onCreateOptionsMenu(menu: Menu?): Boolean {
169 | menuInflater.inflate(R.menu.menu_activity_main, menu)
170 | return super.onCreateOptionsMenu(menu)
171 | }
172 |
173 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
174 | when (item.itemId) {
175 | R.id.scan -> {
176 | startActivity(Intent(this, ScanActivity::class.java))
177 | return true
178 | }
179 | R.id.search -> {
180 | ToastUtils.show("to be completed")
181 | return true
182 | }
183 | }
184 | return super.onOptionsItemSelected(item)
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/activity/ScanActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.activity
2 |
3 | import android.Manifest
4 | import android.app.AlertDialog
5 | import android.content.Context
6 | import android.os.Bundle
7 | import com.sbingo.wanandroid_mvvm.R
8 | import com.sbingo.wanandroid_mvvm.base.BaseActivity
9 | import com.sbingo.wanandroid_mvvm.utils.Listeners
10 | import com.sbingo.wanandroid_mvvm.utils.ToastUtils
11 | import me.dm7.barcodescanner.zbar.ZBarScannerView
12 |
13 | /**
14 | * Author: Sbingo666
15 | * Date: 2019/5/10
16 | */
17 |
18 | class ScanActivity : BaseActivity(), ZBarScannerView.ResultHandler {
19 |
20 | private lateinit var context: Context
21 |
22 | override var layoutId = 0
23 |
24 | override fun initData() {
25 | }
26 |
27 | override fun subscribeUi() {
28 | }
29 |
30 | private lateinit var mScannerView: ZBarScannerView
31 |
32 | public override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 | mScannerView = ZBarScannerView(this)
35 | setContentView(mScannerView)
36 | context = this
37 | checkPermissions(arrayOf(Manifest.permission.CAMERA),
38 | arrayOf("摄像头"),
39 | arrayOf(getString(R.string.camera_permission_reason)),
40 | object : Listeners.PermissionListener {
41 | override fun onGranted() {
42 | mScannerView.setResultHandler(context as ScanActivity)
43 | mScannerView.startCamera()
44 | }
45 |
46 | override fun onDenied(permissions: List) {
47 | ToastUtils.show(getString(R.string.no_permission))
48 | finish()
49 | }
50 |
51 | override fun onShowReason() {
52 | }
53 | })
54 | }
55 |
56 | public override fun onPause() {
57 | super.onPause()
58 | mScannerView.stopCamera()
59 | }
60 |
61 | override fun handleResult(result: me.dm7.barcodescanner.zbar.Result) {
62 | AlertDialog.Builder(this)
63 | .setTitle(R.string.scan_result)
64 | .setMessage(result.contents)
65 | .setCancelable(false)
66 | .setPositiveButton(R.string.i_know, null)
67 | .show()
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/activity/WebActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.activity
2 |
3 | import android.os.Build
4 | import android.view.KeyEvent
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.webkit.WebSettings
8 | import android.webkit.WebView
9 | import android.widget.TextView
10 | import androidx.coordinatorlayout.widget.CoordinatorLayout
11 | import com.google.android.material.appbar.AppBarLayout
12 | import com.just.agentweb.AgentWeb
13 | import com.just.agentweb.DefaultWebClient
14 | import com.just.agentweb.NestedScrollAgentWebView
15 | import com.sbingo.wanandroid_mvvm.Constants
16 | import com.sbingo.wanandroid_mvvm.R
17 | import com.sbingo.wanandroid_mvvm.base.BaseActivity
18 | import kotlinx.android.synthetic.main.activity_web.*
19 | import kotlinx.android.synthetic.main.toolbar.*
20 |
21 | /**
22 | * Author: Sbingo666
23 | * Date: 2019/4/25
24 | */
25 | class WebActivity : BaseActivity() {
26 |
27 | private var agentWeb: AgentWeb? = null
28 | private lateinit var webTitle: String
29 | private lateinit var webUrl: String
30 | private lateinit var errorMsg: TextView
31 | private val mWebView: NestedScrollAgentWebView by lazy {
32 | NestedScrollAgentWebView(this)
33 | }
34 |
35 | override var layoutId = R.layout.activity_web
36 |
37 | override fun initData() {
38 | toolbar.apply {
39 | setSupportActionBar(this)
40 | supportActionBar?.setDisplayHomeAsUpEnabled(true)
41 | }
42 | iv_close.apply {
43 | visibility = View.VISIBLE
44 | setOnClickListener { finish() }
45 | }
46 | tv_title.apply {
47 | visibility = View.VISIBLE
48 | text = getString(R.string.loading)
49 | isSelected = true
50 | }
51 | intent.extras?.apply {
52 | webTitle = getString(Constants.WEB_TITLE, getString(R.string.title_err))
53 | webUrl = getString(Constants.WEB_URL, "")
54 | }
55 | initWebView()
56 | }
57 |
58 | override fun subscribeUi() {
59 | }
60 |
61 | private fun initWebView() {
62 | val layoutParams =
63 | CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
64 | layoutParams.behavior = AppBarLayout.ScrollingViewBehavior()
65 | val errorView = layoutInflater.inflate(R.layout.web_error_page, null)
66 | errorMsg = errorView.findViewById(R.id.error_msg)
67 | errorMsg.text = getString(R.string.web_page_err, webUrl)
68 |
69 | agentWeb = AgentWeb.with(this)
70 | .setAgentWebParent(web_container, 1, layoutParams)
71 | .useDefaultIndicator()
72 | .setWebView(mWebView)
73 | .setWebChromeClient(webChromeClient)
74 | .setMainFrameErrorView(errorView)
75 | .setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)
76 | .createAgentWeb()
77 | .ready()
78 | .go(webUrl)
79 |
80 | agentWeb?.webCreator?.webView?.run {
81 | settings.domStorageEnabled = true
82 | webViewClient = mWebViewClient
83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
84 | settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
85 | }
86 | }
87 | }
88 |
89 | override fun onBackPressed() {
90 | agentWeb?.run {
91 | if (!back()) {
92 | super.onBackPressed()
93 | }
94 | }
95 | }
96 |
97 | override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
98 | return if (agentWeb?.handleKeyEvent(keyCode, event)!!) {
99 | true
100 | } else {
101 | finish()
102 | super.onKeyDown(keyCode, event)
103 | }
104 | }
105 |
106 | override fun onResume() {
107 | agentWeb?.webLifeCycle?.onResume()
108 | super.onResume()
109 | }
110 |
111 | override fun onPause() {
112 | agentWeb?.webLifeCycle?.onPause()
113 | super.onPause()
114 | }
115 |
116 | override fun onDestroy() {
117 | agentWeb?.webLifeCycle?.onDestroy()
118 | super.onDestroy()
119 | }
120 |
121 | private val webChromeClient = object : com.just.agentweb.WebChromeClient() {
122 | override fun onReceivedTitle(view: WebView, title: String) {
123 | super.onReceivedTitle(view, title)
124 | tv_title.text = title
125 | }
126 | }
127 |
128 | private val mWebViewClient = object : com.just.agentweb.WebViewClient() {
129 | }
130 |
131 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import android.graphics.Color
4 | import androidx.lifecycle.Observer
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelProviders
8 | import com.sbingo.wanandroid_mvvm.R
9 | import com.sbingo.wanandroid_mvvm.adapter.HomeAdapter
10 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
11 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
12 | import com.sbingo.wanandroid_mvvm.paging.repository.HomeRepository
13 | import com.sbingo.wanandroid_mvvm.viewmodel.HomeViewModel
14 | import kotlinx.android.synthetic.main.refresh_layout.*
15 |
16 | /**
17 | * Author: Sbingo666
18 | * Date: 2019/4/15
19 | */
20 | class HomeFragment : BaseFragment() {
21 |
22 | private val viewModel by lazy {
23 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
24 | @Suppress("UNCHECKED_CAST")
25 | override fun create(modelClass: Class): T {
26 | val repository = HomeRepository(HttpManager.getInstance())
27 | return HomeViewModel(repository) as T
28 | }
29 | })
30 | .get(HomeViewModel::class.java)
31 | }
32 |
33 | private val adapter by lazy {
34 | HomeAdapter { viewModel.retry() }
35 | }
36 |
37 | override var layoutId = R.layout.refresh_layout
38 |
39 | override fun initData() {
40 | multipleStatusView = multiple_status_view
41 | initSwipe()
42 | initRecyclerView()
43 | }
44 |
45 | override fun subscribeUi() {
46 | viewModel.run {
47 | pagedList.observe(viewLifecycleOwner, Observer {
48 | adapter.submitList(it)
49 | })
50 | refreshState.observe(
51 | viewLifecycleOwner,
52 | Observer {
53 | swipeRefreshLayout.isRefreshing = false
54 | when {
55 | it.isLoading() ->
56 | if (isRefreshFromPull) {
57 | //避免有数据时 view 切换造成的视觉跳动
58 | swipeRefreshLayout.isRefreshing = true
59 | isRefreshFromPull = false
60 | } else {
61 | multipleStatusView?.showLoading()
62 | }
63 | it.isSuccess() ->
64 | if (it.data!!) {
65 | // refreshState 中的 data 表示数据是否为空
66 | multipleStatusView?.showEmpty()
67 | } else {
68 | multipleStatusView?.showContent()
69 | }
70 | it.isError() -> multipleStatusView?.showError()
71 | }
72 | })
73 | networkState.observe(viewLifecycleOwner, Observer {
74 | adapter.setRequestState(it)
75 | })
76 | initLoad()
77 | }
78 | }
79 |
80 | override fun onRetry() {
81 | viewModel.refresh()
82 | }
83 |
84 | private fun initSwipe() {
85 | swipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
86 | swipeRefreshLayout.setOnRefreshListener {
87 | isRefreshFromPull = true
88 | viewModel.refresh()
89 | }
90 | }
91 |
92 | private fun initRecyclerView() {
93 | recyclerView.adapter = adapter
94 | }
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/KnowledgeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import android.graphics.Color
4 | import androidx.lifecycle.Observer
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelProviders
8 | import com.sbingo.wanandroid_mvvm.R
9 | import com.sbingo.wanandroid_mvvm.adapter.HomeAdapter
10 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
11 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
12 | import com.sbingo.wanandroid_mvvm.paging.repository.KnowledgeRepository
13 | import com.sbingo.wanandroid_mvvm.viewmodel.KnowledgeViewModel
14 | import kotlinx.android.synthetic.main.refresh_layout.*
15 |
16 | /**
17 | * Author: Sbingo666
18 | * Date: 2019/4/26
19 | */
20 | class KnowledgeFragment(private val knowledgeId: Int) : BaseFragment() {
21 |
22 | private val viewModel by lazy {
23 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
24 | @Suppress("UNCHECKED_CAST")
25 | override fun create(modelClass: Class): T {
26 | val repository = KnowledgeRepository(HttpManager.getInstance(), knowledgeId)
27 | return KnowledgeViewModel(repository) as T
28 | }
29 | })
30 | .get(KnowledgeViewModel::class.java)
31 | }
32 |
33 | private val adapter by lazy {
34 | HomeAdapter { viewModel.retry() }
35 | }
36 |
37 | override var layoutId = R.layout.refresh_layout
38 |
39 | override fun initData() {
40 | multipleStatusView = multiple_status_view
41 | initSwipe()
42 | initRecyclerView()
43 | }
44 |
45 | override fun subscribeUi() {
46 | viewModel.run {
47 | pagedList.observe(viewLifecycleOwner, Observer {
48 | adapter.submitList(it)
49 | })
50 | refreshState.observe(
51 | viewLifecycleOwner,
52 | Observer {
53 | swipeRefreshLayout.isRefreshing = false
54 | when {
55 | it.isLoading() ->
56 | if (isRefreshFromPull) {
57 | swipeRefreshLayout.isRefreshing = true
58 | isRefreshFromPull = false
59 | } else {
60 | multipleStatusView?.showLoading()
61 | }
62 | it.isSuccess() ->
63 | if (it.data!!) {
64 | // refreshState 中的 data 表示数据是否为空
65 | multipleStatusView?.showEmpty()
66 | } else {
67 | multipleStatusView?.showContent()
68 | }
69 | it.isError() -> multipleStatusView?.showError()
70 | }
71 | })
72 | networkState.observe(viewLifecycleOwner, Observer {
73 | adapter.setRequestState(it)
74 | })
75 | initLoad()
76 | }
77 | }
78 |
79 | override fun onRetry() {
80 | viewModel.refresh()
81 | }
82 |
83 | private fun initSwipe() {
84 | swipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
85 | swipeRefreshLayout.setOnRefreshListener {
86 | isRefreshFromPull = true
87 | viewModel.refresh()
88 | }
89 | }
90 |
91 | private fun initRecyclerView() {
92 | recyclerView.adapter = adapter
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/KnowledgeTreeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import android.graphics.Color
4 | import androidx.lifecycle.Observer
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelProviders
8 | import com.sbingo.wanandroid_mvvm.R
9 | import com.sbingo.wanandroid_mvvm.adapter.KnowledgeTreeAdapter
10 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
11 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
12 | import com.sbingo.wanandroid_mvvm.paging.repository.KnowledgeTreeRepository
13 | import com.sbingo.wanandroid_mvvm.viewmodel.KnowledgeTreeeViewModel
14 | import kotlinx.android.synthetic.main.refresh_layout.*
15 |
16 | /**
17 | * Author: Sbingo666
18 | * Date: 2019/4/15
19 | */
20 | class KnowledgeTreeFragment : BaseFragment() {
21 |
22 | private val viewModel by lazy {
23 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
24 | @Suppress("UNCHECKED_CAST")
25 | override fun create(modelClass: Class): T {
26 | val repository = KnowledgeTreeRepository(HttpManager.getInstance())
27 | return KnowledgeTreeeViewModel(repository) as T
28 | }
29 | })
30 | .get(KnowledgeTreeeViewModel::class.java)
31 | }
32 |
33 | private val adapter by lazy {
34 | KnowledgeTreeAdapter { viewModel.retry() }
35 | }
36 |
37 | override var layoutId = R.layout.refresh_layout
38 |
39 | override fun initData() {
40 | multipleStatusView = multiple_status_view
41 | initSwipe()
42 | initRecyclerView()
43 | }
44 |
45 | override fun subscribeUi() {
46 | viewModel.run {
47 | pagedList.observe(viewLifecycleOwner, Observer {
48 | adapter.submitList(it)
49 | })
50 | refreshState.observe(
51 | viewLifecycleOwner,
52 | Observer {
53 | swipeRefreshLayout.isRefreshing = false
54 | when {
55 | it.isLoading() ->
56 | if (isRefreshFromPull) {
57 | swipeRefreshLayout.isRefreshing = true
58 | isRefreshFromPull = false
59 | } else {
60 | multipleStatusView?.showLoading()
61 | }
62 | it.isSuccess() ->
63 | if (it.data!!) {
64 | // refreshState 中的 data 表示数据是否为空
65 | multipleStatusView?.showEmpty()
66 | } else {
67 | multipleStatusView?.showContent()
68 | }
69 | it.isError() -> multipleStatusView?.showError()
70 | }
71 | })
72 | networkState.observe(viewLifecycleOwner, Observer {
73 | adapter.setRequestState(it)
74 | })
75 | initLoad(50)
76 | }
77 | }
78 |
79 | override fun onRetry() {
80 | viewModel.refresh()
81 | }
82 |
83 | private fun initSwipe() {
84 | swipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
85 | swipeRefreshLayout.setOnRefreshListener {
86 | isRefreshFromPull = true
87 | viewModel.refresh()
88 | }
89 | }
90 |
91 | private fun initRecyclerView() {
92 | recyclerView.adapter = adapter
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/NavigationFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import android.graphics.Color
4 | import androidx.lifecycle.Observer
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelProviders
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import androidx.recyclerview.widget.RecyclerView
10 | import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
11 | import com.orhanobut.logger.Logger
12 | import com.sbingo.wanandroid_mvvm.R
13 | import com.sbingo.wanandroid_mvvm.adapter.NavigationChapterAdapter
14 | import com.sbingo.wanandroid_mvvm.adapter.NavigationWebsiteAdapter
15 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
16 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
17 | import com.sbingo.wanandroid_mvvm.paging.repository.NavigationRepository
18 | import com.sbingo.wanandroid_mvvm.viewmodel.NavigationViewModel
19 | import kotlinx.android.synthetic.main.fragment_navigation.*
20 |
21 | /**
22 | * Author: Sbingo666
23 | * Date: 2019/4/15
24 | */
25 | class NavigationFragment : BaseFragment() {
26 |
27 | private var checkedPosition: Int = 0
28 |
29 | private val viewModel by lazy {
30 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
31 | @Suppress("UNCHECKED_CAST")
32 | override fun create(modelClass: Class): T {
33 | val repository = NavigationRepository(HttpManager.getInstance())
34 | return NavigationViewModel(repository) as T
35 | }
36 | })
37 | .get(NavigationViewModel::class.java)
38 | }
39 |
40 | private val adapterChapter by lazy {
41 | NavigationChapterAdapter(
42 | { viewModel.retry() },
43 | object : NavigationChapterAdapter.OnItemClickListener {
44 | override fun onItemClicked(position: Int) {
45 | position.let {
46 | recyclerView_websites.scrollToPosition(it)
47 | checkedPosition = it
48 | }
49 | }
50 | }
51 | )
52 | }
53 |
54 | private val adapterWebsite by lazy {
55 | NavigationWebsiteAdapter { viewModel.retry() }
56 | }
57 |
58 | override var layoutId = R.layout.fragment_navigation
59 |
60 | override fun initData() {
61 | multipleStatusView = multiple_status_view
62 | initSwipe()
63 | initRecyclerView()
64 | }
65 |
66 | override fun subscribeUi() {
67 | viewModel.run {
68 | pagedList.observe(viewLifecycleOwner, Observer {
69 | adapterChapter.submitList(it)
70 | adapterWebsite.submitList(it)
71 | })
72 | refreshState.observe(
73 | viewLifecycleOwner,
74 | Observer {
75 | swipeRefreshLayout.isRefreshing = false
76 | when {
77 | it.isLoading() ->
78 | if (isRefreshFromPull) {
79 | swipeRefreshLayout.isRefreshing = true
80 | isRefreshFromPull = false
81 | } else {
82 | multipleStatusView?.showLoading()
83 | }
84 | it.isSuccess() ->
85 | if (it.data!!) {
86 | // refreshState 中的 data 表示数据是否为空
87 | multipleStatusView?.showEmpty()
88 | } else {
89 | multipleStatusView?.showContent()
90 | }
91 | it.isError() -> multipleStatusView?.showError()
92 | }
93 | })
94 | networkState.observe(viewLifecycleOwner, Observer {
95 | adapterChapter.setRequestState(it)
96 | adapterWebsite.setRequestState(it)
97 | })
98 | initLoad()
99 | }
100 | }
101 |
102 | override fun onRetry() {
103 | viewModel.refresh()
104 | }
105 |
106 | private fun initSwipe() {
107 | swipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
108 | swipeRefreshLayout.setOnRefreshListener {
109 | isRefreshFromPull = true
110 | viewModel.refresh()
111 | adapterChapter.setCheckedPosition(0)
112 | }
113 | }
114 |
115 | private fun initRecyclerView() {
116 | recyclerView_chapters.adapter = adapterChapter
117 | recyclerView_websites.run {
118 | adapter = adapterWebsite
119 | addOnScrollListener(object : RecyclerView.OnScrollListener() {
120 | override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
121 | if (newState == SCROLL_STATE_IDLE) {
122 | val layoutManager = recyclerView_websites.layoutManager as LinearLayoutManager
123 | val index = layoutManager.findFirstVisibleItemPosition()
124 | if (checkedPosition != index) {
125 | index.let {
126 | Logger.d("改变导航 position:$checkedPosition --> $it")
127 | recyclerView_chapters.scrollToPosition(it)
128 | adapterChapter.setCheckedPosition(it)
129 | checkedPosition = it
130 | }
131 | }
132 | }
133 | }
134 | })
135 | }
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/ProjectArticleFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import android.graphics.Color
4 | import androidx.lifecycle.Observer
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelProviders
8 | import com.sbingo.wanandroid_mvvm.R
9 | import com.sbingo.wanandroid_mvvm.adapter.HomeAdapter
10 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
11 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
12 | import com.sbingo.wanandroid_mvvm.paging.repository.ProjectArticleRepository
13 | import com.sbingo.wanandroid_mvvm.viewmodel.ProjectArticleViewModel
14 | import kotlinx.android.synthetic.main.refresh_layout.*
15 |
16 | /**
17 | * Author: Sbingo666
18 | * Date: 2019/4/23
19 | */
20 | class ProjectArticleFragment(private val projectId: Int) : BaseFragment() {
21 |
22 | private val viewModel by lazy {
23 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
24 | @Suppress("UNCHECKED_CAST")
25 | override fun create(modelClass: Class): T {
26 | val repository = ProjectArticleRepository(HttpManager.getInstance(), projectId)
27 | return ProjectArticleViewModel(repository) as T
28 | }
29 | })
30 | .get(ProjectArticleViewModel::class.java)
31 | }
32 |
33 | private val adapter by lazy {
34 | HomeAdapter { viewModel.retry() }
35 | }
36 |
37 | override var layoutId = R.layout.refresh_layout
38 |
39 | override fun initData() {
40 | multipleStatusView = multiple_status_view
41 | initSwipe()
42 | initRecyclerView()
43 | }
44 |
45 | override fun subscribeUi() {
46 | viewModel.run {
47 | pagedList.observe(viewLifecycleOwner, Observer {
48 | adapter.submitList(it)
49 | })
50 | refreshState.observe(
51 | viewLifecycleOwner,
52 | Observer {
53 | swipeRefreshLayout.isRefreshing = false
54 | when {
55 | it.isLoading() ->
56 | if (isRefreshFromPull) {
57 | swipeRefreshLayout.isRefreshing = true
58 | isRefreshFromPull = false
59 | } else {
60 | multipleStatusView?.showLoading()
61 | }
62 | it.isSuccess() ->
63 | if (it.data!!) {
64 | // refreshState 中的 data 表示数据是否为空
65 | multipleStatusView?.showEmpty()
66 | } else {
67 | multipleStatusView?.showContent()
68 | }
69 | it.isError() -> multipleStatusView?.showError()
70 | }
71 | })
72 | networkState.observe(viewLifecycleOwner, Observer {
73 | adapter.setRequestState(it)
74 | })
75 | initLoad()
76 | }
77 | }
78 |
79 | override fun onRetry() {
80 | viewModel.refresh()
81 | }
82 |
83 | private fun initSwipe() {
84 | swipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
85 | swipeRefreshLayout.setOnRefreshListener {
86 | isRefreshFromPull = true
87 | viewModel.refresh()
88 | }
89 | }
90 |
91 | private fun initRecyclerView() {
92 | recyclerView.adapter = adapter
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/ProjectFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import androidx.lifecycle.ViewModelProviders
6 | import com.google.android.material.tabs.TabLayout
7 | import com.sbingo.wanandroid_mvvm.R
8 | import com.sbingo.wanandroid_mvvm.adapter.ProjectViewPagerAdapter
9 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
10 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
11 | import com.sbingo.wanandroid_mvvm.repository.ProjectRepository
12 | import com.sbingo.wanandroid_mvvm.viewmodel.ProjectViewModel
13 | import kotlinx.android.synthetic.main.fragment_tab_vp.*
14 |
15 | /**
16 | * Author: Sbingo666
17 | * Date: 2019/4/15
18 | */
19 | class ProjectFragment : BaseFragment() {
20 |
21 | private val viewModel by lazy {
22 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
23 | @Suppress("UNCHECKED_CAST")
24 | override fun create(modelClass: Class): T {
25 | val repository = ProjectRepository(HttpManager.getInstance())
26 | return ProjectViewModel(repository) as T
27 | }
28 | })
29 | .get(ProjectViewModel::class.java)
30 | }
31 |
32 | private val adapter by lazy {
33 | ProjectViewPagerAdapter(childFragmentManager)
34 | }
35 |
36 | override var layoutId = R.layout.fragment_tab_vp
37 |
38 | override fun initData() {
39 | multipleStatusView = multiple_status_view
40 | viewPager.adapter = adapter
41 | tabLayout.run {
42 | setupWithViewPager(viewPager)
43 | addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
44 | override fun onTabReselected(tab: TabLayout.Tab?) {
45 | }
46 |
47 | override fun onTabUnselected(tab: TabLayout.Tab?) {
48 | }
49 |
50 | override fun onTabSelected(tab: TabLayout.Tab?) {
51 | tab?.let {
52 | viewPager.setCurrentItem(it.position, false)
53 | }
54 | }
55 | })
56 | }
57 | }
58 |
59 | override fun subscribeUi() {
60 | handleData(viewModel.projects) {
61 | adapter.setData(it)
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/WXArticleFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import android.graphics.Color
4 | import androidx.lifecycle.Observer
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelProviders
8 | import com.sbingo.wanandroid_mvvm.R
9 | import com.sbingo.wanandroid_mvvm.adapter.HomeAdapter
10 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
11 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
12 | import com.sbingo.wanandroid_mvvm.paging.repository.WXRepository
13 | import com.sbingo.wanandroid_mvvm.viewmodel.WXArticleViewModel
14 | import kotlinx.android.synthetic.main.refresh_layout.*
15 |
16 | /**
17 | * Author: Sbingo666
18 | * Date: 2019/4/22
19 | */
20 | class WXArticleFragment(private val wxId: Int) : BaseFragment() {
21 |
22 | private val viewModel by lazy {
23 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
24 | @Suppress("UNCHECKED_CAST")
25 | override fun create(modelClass: Class): T {
26 | val repository = WXRepository(HttpManager.getInstance(), wxId)
27 | return WXArticleViewModel(repository) as T
28 | }
29 | })
30 | .get(WXArticleViewModel::class.java)
31 | }
32 |
33 | private val adapter by lazy {
34 | HomeAdapter { viewModel.retry() }
35 | }
36 |
37 | override var layoutId = R.layout.refresh_layout
38 |
39 | override fun initData() {
40 | multipleStatusView = multiple_status_view
41 | initSwipe()
42 | initRecyclerView()
43 | }
44 |
45 | override fun subscribeUi() {
46 | viewModel.run {
47 | pagedList.observe(viewLifecycleOwner, Observer {
48 | adapter.submitList(it)
49 | })
50 | refreshState.observe(
51 | viewLifecycleOwner,
52 | Observer {
53 | swipeRefreshLayout.isRefreshing = false
54 | when {
55 | it.isLoading() ->
56 | if (isRefreshFromPull) {
57 | swipeRefreshLayout.isRefreshing = true
58 | isRefreshFromPull = false
59 | } else {
60 | multipleStatusView?.showLoading()
61 | }
62 | it.isSuccess() ->
63 | if (it.data!!) {
64 | // refreshState 中的 data 表示数据是否为空
65 | multipleStatusView?.showEmpty()
66 | } else {
67 | multipleStatusView?.showContent()
68 | }
69 | it.isError() -> multipleStatusView?.showError()
70 | }
71 | })
72 | networkState.observe(viewLifecycleOwner, Observer {
73 | adapter.setRequestState(it)
74 | })
75 | initLoad()
76 | }
77 | }
78 |
79 | override fun onRetry() {
80 | viewModel.refresh()
81 | }
82 |
83 | private fun initSwipe() {
84 | swipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
85 | swipeRefreshLayout.setOnRefreshListener {
86 | isRefreshFromPull = true
87 | viewModel.refresh()
88 | }
89 | }
90 |
91 | private fun initRecyclerView() {
92 | recyclerView.adapter = adapter
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/ui/fragment/WeChatFragment.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.ui.fragment
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import androidx.lifecycle.ViewModelProviders
6 | import com.google.android.material.tabs.TabLayout
7 | import com.sbingo.wanandroid_mvvm.R
8 | import com.sbingo.wanandroid_mvvm.adapter.WeChatViewPagerAdapter
9 | import com.sbingo.wanandroid_mvvm.base.BaseFragment
10 | import com.sbingo.wanandroid_mvvm.data.http.HttpManager
11 | import com.sbingo.wanandroid_mvvm.repository.WeChatRepository
12 | import com.sbingo.wanandroid_mvvm.viewmodel.WeChatViewModel
13 | import kotlinx.android.synthetic.main.fragment_tab_vp.*
14 |
15 | /**
16 | * Author: Sbingo666
17 | * Date: 2019/4/15
18 | */
19 | class WeChatFragment : BaseFragment() {
20 |
21 | private val viewModel by lazy {
22 | ViewModelProviders.of(this, object : ViewModelProvider.Factory {
23 | @Suppress("UNCHECKED_CAST")
24 | override fun create(modelClass: Class): T {
25 | val repository = WeChatRepository(HttpManager.getInstance())
26 | return WeChatViewModel(repository) as T
27 | }
28 | })
29 | .get(WeChatViewModel::class.java)
30 | }
31 |
32 | private val adapter by lazy {
33 | WeChatViewPagerAdapter(childFragmentManager)
34 | }
35 |
36 | override var layoutId = R.layout.fragment_tab_vp
37 |
38 | override fun initData() {
39 | multipleStatusView = multiple_status_view
40 | viewPager.adapter = adapter
41 | tabLayout.run {
42 | setupWithViewPager(viewPager)
43 | addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
44 | override fun onTabReselected(tab: TabLayout.Tab?) {
45 | }
46 |
47 | override fun onTabUnselected(tab: TabLayout.Tab?) {
48 | }
49 |
50 | override fun onTabSelected(tab: TabLayout.Tab?) {
51 | tab?.let {
52 | viewPager.setCurrentItem(it.position, false)
53 | }
54 | }
55 | })
56 | }
57 | }
58 |
59 | override fun subscribeUi() {
60 | handleData(viewModel.wxChapters) {
61 | adapter.setData(it)
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/ExecutorUtils.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import java.util.concurrent.Executors
6 |
7 | object ExecutorUtils {
8 |
9 | val DISK_IO = Executors.newSingleThreadExecutor()
10 |
11 | val NETWORK_IO = Executors.newFixedThreadPool(5)
12 |
13 | fun main_thread(runnable: Runnable, delay: Long = 0) {
14 | Handler(Looper.getMainLooper())
15 | .postDelayed(runnable, delay)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/Ext.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | import com.sbingo.wanandroid_mvvm.data.http.RxHttpObserver
4 | import io.reactivex.Observable
5 | import io.reactivex.android.schedulers.AndroidSchedulers
6 | import io.reactivex.schedulers.Schedulers
7 |
8 | /**
9 | * Author: Sbingo666
10 | * Date: 2019/4/23
11 | */
12 |
13 | fun Observable.async(): Observable {
14 | return this.subscribeOn(Schedulers.io())
15 | .observeOn(AndroidSchedulers.mainThread())
16 | }
17 |
18 | fun Observable.asyncSubscribe(onNext: (T) -> Unit, onError: (Throwable) -> Unit) {
19 | this.async()
20 | .subscribe(object : RxHttpObserver() {
21 | override fun onNext(it: T) {
22 | super.onNext(it)
23 | onNext(it)
24 | }
25 |
26 | override fun onError(e: Throwable) {
27 | super.onError(e)
28 | onError(e)
29 | }
30 | })
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/ImageLoader.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | import android.content.Context
4 | import android.widget.ImageView
5 | import com.bumptech.glide.Glide
6 | import com.bumptech.glide.load.engine.DiskCacheStrategy
7 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
8 | import com.bumptech.glide.request.RequestOptions
9 | import com.sbingo.wanandroid_mvvm.R
10 |
11 | /**
12 | * Author: Sbingo666
13 | * Date: 2019/4/18
14 | */
15 | object ImageLoader {
16 |
17 | fun load(context: Context?, url: String?, iv: ImageView?) {
18 | iv?.apply {
19 | Glide.with(context!!).clear(iv)
20 | val options = RequestOptions()
21 | .diskCacheStrategy(DiskCacheStrategy.DATA)
22 | .placeholder(R.drawable.bg_placeholder)
23 | .error(R.drawable.pic_error)
24 | Glide.with(context)
25 | .load(url)
26 | .transition(DrawableTransitionOptions().crossFade())
27 | .apply(options)
28 | .into(iv)
29 | }
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/Listeners.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | /**
4 | * Author: Sbingo666
5 | * Date: 2019/5/13
6 | */
7 | object Listeners {
8 | interface PermissionListener {
9 | fun onGranted()
10 |
11 | fun onDenied(permissions: List)
12 |
13 | fun onShowReason()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/NetUtils.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | import android.content.Context
4 | import android.net.ConnectivityManager
5 | import android.net.wifi.WifiManager
6 | import android.telephony.TelephonyManager
7 | import android.text.TextUtils
8 |
9 | class NetUtils private constructor() {
10 |
11 | companion object {
12 | /**
13 | * 没有网络
14 | */
15 | val NETWORK_TYPE_INVALID = 0
16 | /**
17 | * wap网络
18 | */
19 | val NETWORK_TYPE_WAP = 1
20 | /**
21 | * 2G网络
22 | */
23 | val NETWORK_TYPE_2G = 2
24 | /**
25 | * 3G和3G以上网络,或统称为快速网络
26 | */
27 | val NETWORK_TYPE_3G = 3
28 | /**
29 | * wifi网络
30 | */
31 | val NETWORK_TYPE_WIFI = 4
32 |
33 | /**
34 | * 判断网络是否连接
35 | *
36 | * @param context
37 | * @return
38 | */
39 | fun isConnected(context: Context): Boolean {
40 | val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
41 | val ni = cm.activeNetworkInfo
42 | return ni != null && ni.isConnected
43 | }
44 |
45 | /**
46 | * 判断是否是wifi连接
47 | */
48 | fun isWifi(context: Context): Boolean {
49 | return getNetWorkType(context) == NETWORK_TYPE_WIFI
50 | }
51 |
52 | fun getWifiName(context: Context): String {
53 | val wifiInfo =
54 | (context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager).connectionInfo
55 | return if (wifiInfo != null) wifiInfo.ssid else ""
56 | }
57 |
58 | /**
59 | * 获取网络状态,wifi,wap,2g,3g.
60 | *
61 | * @param context 上下文
62 | * @return int 网络状态 [.NETWORK_TYPE_2G],[.NETWORK_TYPE_3G],
63 | * [.NETWORK_TYPE_INVALID],[.NETWORK_TYPE_WAP],[.NETWORK_TYPE_WIFI]
64 | */
65 | fun getNetWorkType(context: Context): Int {
66 | var netWorkType = -1
67 | val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
68 | val networkInfo = manager.activeNetworkInfo
69 | if (networkInfo != null && networkInfo.isConnected) {
70 | val type = networkInfo.typeName
71 | if (type.equals("WIFI", ignoreCase = true)) {
72 | netWorkType = NETWORK_TYPE_WIFI
73 | } else if (type.equals("MOBILE", ignoreCase = true)) {
74 | val proxyHost = android.net.Proxy.getDefaultHost()
75 | netWorkType = if (TextUtils.isEmpty(proxyHost))
76 | if (isFastMobileNetwork(context)) NETWORK_TYPE_3G else NETWORK_TYPE_2G
77 | else
78 | NETWORK_TYPE_WAP
79 | }
80 | } else {
81 | netWorkType = NETWORK_TYPE_INVALID
82 | }
83 | return netWorkType
84 | }
85 |
86 | private fun isFastMobileNetwork(context: Context): Boolean {
87 | val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
88 | when (telephonyManager.networkType) {
89 | TelephonyManager.NETWORK_TYPE_1xRTT -> return false // ~ 50-100 kbps
90 | TelephonyManager.NETWORK_TYPE_CDMA -> return false // ~ 14-64 kbps
91 | TelephonyManager.NETWORK_TYPE_EDGE -> return false // ~ 50-100 kbps
92 | TelephonyManager.NETWORK_TYPE_EVDO_0 -> return true // ~ 400-1000 kbps
93 | TelephonyManager.NETWORK_TYPE_EVDO_A -> return true // ~ 600-1400 kbps
94 | TelephonyManager.NETWORK_TYPE_GPRS -> return false // ~ 100 kbps
95 | TelephonyManager.NETWORK_TYPE_HSDPA -> return true // ~ 2-14 Mbps
96 | TelephonyManager.NETWORK_TYPE_HSPA -> return true // ~ 700-1700 kbps
97 | TelephonyManager.NETWORK_TYPE_HSUPA -> return true // ~ 1-23 Mbps
98 | TelephonyManager.NETWORK_TYPE_UMTS -> return true // ~ 400-7000 kbps
99 | TelephonyManager.NETWORK_TYPE_EHRPD -> return true // ~ 1-2 Mbps
100 | TelephonyManager.NETWORK_TYPE_EVDO_B -> return true // ~ 5 Mbps
101 | TelephonyManager.NETWORK_TYPE_HSPAP -> return true // ~ 10-20 Mbps
102 | TelephonyManager.NETWORK_TYPE_IDEN -> return false // ~25 kbps
103 | TelephonyManager.NETWORK_TYPE_LTE -> return true // ~ 10+ Mbps
104 | TelephonyManager.NETWORK_TYPE_UNKNOWN -> return false
105 | else -> return false
106 | }
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/PreferenceUtils.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.content.SharedPreferences
6 | import com.sbingo.wanandroid_mvvm.WanApplication
7 | import java.io.*
8 | import kotlin.reflect.KProperty
9 |
10 | /**
11 | * Author: Sbingo666
12 | * Date: 2019/4/3
13 | */
14 | class PreferenceUtils(val name: String, private val default: T) {
15 |
16 | companion object {
17 | private val file_name = "wanandroid_mvvm"
18 |
19 | private val pref: SharedPreferences by lazy {
20 | WanApplication.instance.getSharedPreferences(file_name, Context.MODE_PRIVATE)
21 | }
22 |
23 | fun clearAll() {
24 | pref.edit().clear().apply()
25 | }
26 |
27 | fun clear(key: String) {
28 | pref.edit().remove(key).apply()
29 | }
30 |
31 | fun contains(key: String): Boolean {
32 | return pref.contains(key)
33 | }
34 |
35 | fun getAll(): Map {
36 | return pref.all
37 | }
38 | }
39 |
40 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
41 | return getSharedPreferences(name, default)
42 | }
43 |
44 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
45 | putSharedPreferences(name, value)
46 | }
47 |
48 | @SuppressLint("CommitPrefEdits")
49 | private fun putSharedPreferences(name: String, value: T) = with(pref.edit()) {
50 | when (value) {
51 | is Long -> putLong(name, value)
52 | is String -> putString(name, value)
53 | is Int -> putInt(name, value)
54 | is Boolean -> putBoolean(name, value)
55 | is Float -> putFloat(name, value)
56 | else -> putString(name, serialize(value))
57 | }.apply()
58 | }
59 |
60 | @Suppress("UNCHECKED_CAST")
61 | private fun getSharedPreferences(name: String, default: T): T = with(pref) {
62 | val res: Any = when (default) {
63 | is Long -> getLong(name, default)
64 | is String -> getString(name, default)
65 | is Int -> getInt(name, default)
66 | is Boolean -> getBoolean(name, default)
67 | is Float -> getFloat(name, default)
68 | else -> deSerialization(getString(name, serialize(default)))
69 | }
70 | return res as T
71 | }
72 |
73 | @Throws(IOException::class)
74 | private fun serialize(obj: A): String {
75 | val byteArrayOutputStream = ByteArrayOutputStream()
76 | val objectOutputStream = ObjectOutputStream(
77 | byteArrayOutputStream)
78 | objectOutputStream.writeObject(obj)
79 | var serStr = byteArrayOutputStream.toString("ISO-8859-1")
80 | serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
81 | objectOutputStream.close()
82 | byteArrayOutputStream.close()
83 | return serStr
84 | }
85 |
86 | @Suppress("UNCHECKED_CAST")
87 | @Throws(IOException::class, ClassNotFoundException::class)
88 | private fun deSerialization(str: String): A {
89 | val redStr = java.net.URLDecoder.decode(str, "UTF-8")
90 | val byteArrayInputStream = ByteArrayInputStream(
91 | redStr.toByteArray(charset("ISO-8859-1")))
92 | val objectInputStream = ObjectInputStream(
93 | byteArrayInputStream)
94 | val obj = objectInputStream.readObject() as A
95 | objectInputStream.close()
96 | byteArrayInputStream.close()
97 | return obj
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/utils/ToastUtils.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.utils
2 |
3 | import android.view.Gravity
4 | import android.view.LayoutInflater
5 | import android.widget.TextView
6 | import android.widget.Toast
7 | import com.sbingo.wanandroid_mvvm.R
8 | import com.sbingo.wanandroid_mvvm.WanApplication
9 |
10 |
11 | class ToastUtils {
12 |
13 | companion object {
14 | private var toast: Toast? = null
15 |
16 | fun show(content: String) {
17 | val inflater = LayoutInflater.from(WanApplication.instance)
18 | val view = inflater.inflate(R.layout.toast_layout, null)
19 | val text = view.findViewById(R.id.toast_text) as TextView
20 | text.text = content
21 | toast = Toast(WanApplication.instance)
22 | toast!!.setGravity(Gravity.CENTER, 0, 0)
23 | toast!!.duration = Toast.LENGTH_SHORT
24 | toast!!.view = view
25 | toast!!.show()
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/HomeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingViewModel
4 | import com.sbingo.wanandroid_mvvm.model.Article
5 | import com.sbingo.wanandroid_mvvm.paging.repository.HomeRepository
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/18
10 | */
11 | class HomeViewModel(repository: HomeRepository) : BasePagingViewModel(repository) {
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/KnowledgeTreeeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingViewModel
4 | import com.sbingo.wanandroid_mvvm.model.Chapter
5 | import com.sbingo.wanandroid_mvvm.paging.repository.KnowledgeTreeRepository
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/26
10 | */
11 | class KnowledgeTreeeViewModel(repository: KnowledgeTreeRepository) :BasePagingViewModel(repository){
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/KnowledgeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingViewModel
4 | import com.sbingo.wanandroid_mvvm.model.Article
5 | import com.sbingo.wanandroid_mvvm.paging.repository.KnowledgeRepository
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/26
10 | */
11 | class KnowledgeViewModel(repository: KnowledgeRepository) :BasePagingViewModel(repository){
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/NavigationViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingViewModel
4 | import com.sbingo.wanandroid_mvvm.model.Navigation
5 | import com.sbingo.wanandroid_mvvm.paging.repository.NavigationRepository
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/28
10 | */
11 | class NavigationViewModel(repository: NavigationRepository) :BasePagingViewModel(repository){
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/ProjectArticleViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingViewModel
4 | import com.sbingo.wanandroid_mvvm.model.Article
5 | import com.sbingo.wanandroid_mvvm.paging.repository.ProjectArticleRepository
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/18
10 | */
11 | class ProjectArticleViewModel(repository: ProjectArticleRepository) :BasePagingViewModel(repository){
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/ProjectViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.sbingo.wanandroid_mvvm.repository.ProjectRepository
5 |
6 | /**
7 | * Author: Sbingo666
8 | * Date: 2019/4/23
9 | */
10 | class ProjectViewModel(private val repository: ProjectRepository) : ViewModel() {
11 |
12 | val projects = repository.getProjects()
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/WXArticleViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import com.sbingo.wanandroid_mvvm.base.paging.BasePagingViewModel
4 | import com.sbingo.wanandroid_mvvm.model.Article
5 | import com.sbingo.wanandroid_mvvm.paging.repository.WXRepository
6 |
7 | /**
8 | * Author: Sbingo666
9 | * Date: 2019/4/18
10 | */
11 | class WXArticleViewModel(repository: WXRepository) :BasePagingViewModel(repository){
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sbingo/wanandroid_mvvm/viewmodel/WeChatViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.sbingo.wanandroid_mvvm.repository.WeChatRepository
5 |
6 | /**
7 | * Author: Sbingo666
8 | * Date: 2019/4/22
9 | */
10 | class WeChatViewModel(private val repository: WeChatRepository) : ViewModel() {
11 |
12 | val wxChapters = repository.getWXChapters()
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/push_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/push_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_fresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_placeholder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_dashboard_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_navigation.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notifications_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_project.xml:
--------------------------------------------------------------------------------
1 |
6 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_scan.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_wechat.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
7 |
9 |
11 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/nav_item_color_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pic_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/src/main/res/drawable/pic_error.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/round_toast_bg.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_knowledge_tree.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
32 |
33 |
34 |
35 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_web.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/banner_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/container.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/empty_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/error_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/footer_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
26 |
36 |
37 |
43 |
44 |
51 |
52 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
14 |
17 |
18 |
25 |
31 |
32 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_tab_vp.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
13 |
24 |
25 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/home_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
26 |
27 |
40 |
41 |
54 |
55 |
64 |
65 |
83 |
84 |
98 |
99 |
109 |
110 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/knowledge_tree_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
27 |
28 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/loading_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
28 |
29 |
33 |
34 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/navigation_tv.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/navigation_website.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/navigation_website_tv.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/refresh_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toast_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
26 |
27 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/web_error_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/navigation_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_wan_mvvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/src/main/res/mipmap-hdpi/ic_wan_mvvm.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_wan_mvvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/src/main/res/mipmap-mdpi/ic_wan_mvvm.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_wan_mvvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/src/main/res/mipmap-xhdpi/ic_wan_mvvm.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_wan_mvvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/src/main/res/mipmap-xxhdpi/ic_wan_mvvm.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_wan_mvvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/src/main/res/mipmap-xxxhdpi/ic_wan_mvvm.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #000000
5 | #f44336
6 | #e91e63
7 | #9c27b0
8 | #673ab7
9 | #3f51b5
10 | #2196f3
11 | #03a9f4
12 | #00bcd4
13 | #009688
14 | #4caf50
15 | #8bc34a
16 | #cddc39
17 | #ffeb3b
18 | #ffc107
19 | #ff9800
20 | #ff5722
21 | #795548
22 | #9e9e9e
23 | #607d8b
24 |
25 | #2196F3
26 | #1976D2
27 | #BBDEFB
28 | #448AFF
29 | #212121
30 | #757575
31 | #BDBDBD
32 | #00000000
33 | @color/White
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 20sp
7 | 18sp
8 | 16sp
9 | 14sp
10 | 12sp
11 | 10sp
12 |
13 | 180dp
14 | 140dp
15 | 120dp
16 | 100dp
17 | 80dp
18 | 60dp
19 | 40dp
20 | 20dp
21 | 18dp
22 | 16dp
23 | 14dp
24 | 12dp
25 | 10dp
26 | 8dp
27 | 6dp
28 | 5dp
29 | 4dp
30 | 2dp
31 | 1dp
32 | 0.5dp
33 | 0dp
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WanAndroid
3 |
4 | 首页
5 | 公众号
6 | 项目
7 | 导航
8 | 知识体系
9 |
10 | 收藏
11 | 待办清单
12 | 夜间模式
13 | 设置
14 | 退出登录
15 | 关于
16 |
17 | 重新加载
18 | 加载中…
19 | 未知标题
20 | 网页 %s 加载失败 o(╥﹏╥)o
21 | 暂无数据 o(╥﹏╥)o \n 点击屏幕重新加载
22 | 加载失败 o(╥﹏╥)o \n 点击屏幕重新加载
23 |
24 | 再按一次退出
25 | 网络连接不可用,请检查网络!
26 | 业务处理失败,后台未返回具体原因!
27 |
28 | 用户名
29 |
30 | 新
31 | 置顶
32 | 搜索
33 | 扫码
34 | 扫码结果
35 | 我知道了
36 | 开启摄像头权限才能扫码,请授予权限以便正常使用
37 | 缺少必要权限,该功能无法正常使用
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/test/java/com/sbingo/wanandroid_mvvm/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.sbingo.wanandroid_mvvm
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/wanandroid-mvvm.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/app/wanandroid-mvvm.jks
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | apply from: "config.gradle"
4 |
5 | buildscript {
6 | ext.kotlin_version = '1.3.21'
7 | repositories {
8 | google()
9 | jcenter()
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.4.0'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | android = [
3 | compileSdkVersion: 28,
4 | minSdkVersion : 21,
5 | targetSdkVersion : 28,
6 | versionCode : 1,
7 | versionName : "1.0.0"
8 | ]
9 |
10 | dependVersion = [
11 | support : "28.0.0",
12 | lifecycle : "2.1.0-alpha02",
13 | navigation: "2.0.0",
14 | retrofit : "2.4.0",
15 | glide : "4.8.0"
16 | ]
17 |
18 | depend = [
19 | "appcompat" : "androidx.appcompat:appcompat:1.1.0-alpha03",
20 | "support-v4" : "com.android.support:support-v4:${dependVersion.support}",
21 | "appcompatv7" : "com.android.support:appcompat-v7:${dependVersion.support}",
22 | "design" : "com.android.support:design:${dependVersion.support}",
23 | "multidex" : "com.android.support:multidex:1.0.3",
24 | "core-ktx" : "androidx.core:core-ktx:1.1.0-alpha05",
25 | "material" : "com.google.android.material:material:1.1.0-alpha05",
26 | "constraintlayout" : "androidx.constraintlayout:constraintlayout:1.1.3",
27 | "lifecycle-extensions" : "androidx.lifecycle:lifecycle-extensions:${dependVersion.lifecycle}",
28 | "lifecycle-viewmodel-ktx": "androidx.lifecycle:lifecycle-viewmodel-ktx:${dependVersion.lifecycle}",
29 | "navigation-fragment-ktx": "androidx.navigation:navigation-fragment-ktx:${dependVersion.navigation}",
30 | "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${dependVersion.navigation}",
31 | "paging_ktx" : "androidx.paging:paging-runtime-ktx:2.1.0-rc01",
32 | "recyclerview" : "androidx.recyclerview:recyclerview:1.1.0-alpha02",
33 | "glide" : "com.github.bumptech.glide:glide:${dependVersion.glide}",
34 | "retrofit" : "com.squareup.retrofit2:retrofit:${dependVersion.retrofit}",
35 | "adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofit}",
36 | "converter-gson" : "com.squareup.retrofit2:converter-gson:${dependVersion.retrofit}",
37 | "converter-scalars" : "com.squareup.retrofit2:converter-scalars:${dependVersion.retrofit}",
38 | "okhttp" : "com.squareup.okhttp3:okhttp:3.4.2",
39 | "rxjava" : "io.reactivex.rxjava2:rxjava:2.2.8",
40 | "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.1",
41 | "logger" : "com.orhanobut:logger:1.15",
42 | "agentweb" : "com.just.agentweb:agentweb:4.0.3-alpha@aar",
43 | "flowlayout" : "com.hyman:flowlayout-lib:1.1.2",
44 | "banner" : 'cn.bingoogolapple:bga-banner:2.2.6@aar',
45 | "zbar" : "me.dm7.barcodescanner:zbar:1.9",
46 | "multiple-status-view" : "com.classic.common:multiple-status-view:1.6",
47 | ]
48 |
49 | libraries = depend.values()
50 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
23 | RELEASE_STORE_FILE=wanandroid-mvvm.jks
24 | RELEASE_STORE_PASSWORD=sbingo666
25 | RELEASE_KEY_ALIAS=wanandroid-mvvm
26 | RELEASE_KEY_PASSWORD=sbingo666
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Apr 19 10:13:22 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/images/arc.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/arc.PNG
--------------------------------------------------------------------------------
/images/wan2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan2.png
--------------------------------------------------------------------------------
/images/wan3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan3.png
--------------------------------------------------------------------------------
/images/wan4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan4.png
--------------------------------------------------------------------------------
/images/wan5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan5.png
--------------------------------------------------------------------------------
/images/wan6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan6.png
--------------------------------------------------------------------------------
/images/wan7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan7.png
--------------------------------------------------------------------------------
/images/wan8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan8.png
--------------------------------------------------------------------------------
/images/wan9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binaryshao/WanAndroid-MVVM/837a58a4fed8071cc6ca0065e5af66b07a5c5906/images/wan9.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------