├── .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 | ![as彬哥六六六](https://s2.ax1x.com/2019/03/26/AapwMq.jpg) 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 |